discord水群bot教程(1) 双号互聊

韭菜的第一篇文章,萌新写手,多多担待

前言

本篇文章主要是实现一个discord自动水群机器人

计划功能大概就是 话术抓取、双号互聊、关键词检测、mod发言检测等一些市面上discord bot比较常见的一些功能,最后会提供一个可以开箱即用的带GUI的成品程序(大概),教程的代码会同步更新在Github上

教程有些许技术门槛,主要涉及node.js和一些依赖库的使用,基本上有入门的基础就可以看懂了,不会的小伙伴可以自学下node.js或者等后续成品exe

教程开始

第一篇主要是实现话术抓取以及双号互聊这两个功能,先来实现话术抓取

首先准备好node环境,在终端内输入以下命令

mkdir discord-bot

cd discord-bot

npm init-y

npm install axios

新建一个getChatRecord.js,话术抓取需要使用http请求获取频道内的聊天记录然后将内容保存到本地,所以我们需要在js内引入http模块 axios 和 文件系统模块fs

const axios = require("axios");
const fs = require('fs');

要使用http获取聊天记录我们需要知道具体请求的url

在浏览器内按F12打开控制台,找到network栏,然后点击clear图标清除历史请求记录

post image

接着我们在discord进入想要抓取聊天记录的频道,然后在控制台找到一条name为message的请求,点击请求后可以查看Preview可以看到请求返回的内容就是频道内的聊天记录

post image

ok,那么这条请求就是获取聊天记录的请求,我们点击Headers可以看到这个请求是一个get请求,参数也在请求url内,这一串数字就是频道id,后面limit就是这次查询获取的数量,Request Headers中的authorization就是你的用户token,知道url,参数,鉴权我们就可以自己构建请求了

post image

具体实现

const axios = require("axios");
const fs = require('fs');
const authorization = ‘你的authorization’

const proxyHost = '127.0.0.1' // 代理ip
const proxyPort = '7890' // 代理端口号

/**
 * 爬取指定频道聊天记录(爬取话术)
 * @param {*} channel_id 频道id
 * @param {*} amount 抓取数量(50的倍数)
 */
 const getChatRecord = async (channel_id,amount=50,) => {
    return new Promise(async (resolve, reject) => {
        let recordList = []
        let beforeId = ''
        const getChatRecordLimit = () => {
            let beforeQuery = beforeId?`before=${beforeId}&`:''
            let header = {
                "Authorization": authorization,
                "Content-Type": "application/json",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36"
            }
            axios({
                method: 'GET',
                url: `https://discord.com/api/v9/channels/${channel_id}/messages?${beforeQuery}limit=50`,
                headers: header,
                proxy: (proxyHost && proxyPort)?{
                    host: proxyHost,
                    port: proxyPort
                }: null,
            }).then(res => {
                let data = res.data
                data.forEach(v => {
                    recordList.push(v.content)
                })
                if(recordList.length<amount){
                    beforeId = data[data.length-1].id
                    getChatRecordLimit()
                }else{
                    recordList.forEach(v => {
                        writeOutput(`${v}\n`);
                    })
                    console.log('话术抓取成功')
                    resolve()
                }
            }).catch(e => {
                reject(e)
                console.log(e.message)
            })
        }
        getChatRecordLimit()
    })
}

function writeOutput(data) {
    fs.appendFile('话术.txt', data, function (err) {
        if (err) throw err;
    });
}

getChatRecord('频道id',数量)

因为discord默认请求查询数量是50,所以需要大量的聊天记录的话我就不篡改他的请求数量了,而是通过循环调用来实现查询指定数量的聊天记录

writeOutput函数是利用fs模块将获取到的聊天内容保存到当前目录的话术.txt内,这样我们的话术抓取功能就实现好了,我们再终端输入node getChatRecord.js就可以运行了

话术抓取完毕后我们继续看看互聊的实现

既然是互聊那肯定需要发送普通消息和发送回复消息,我们打开F12控制台,在频道内输入文字后还是找到一条name为messages的请求

post image

这次我们可以看到是一个post请求,url内只包含了频道id,那么我们切换到Payload看下传参

post image

可以看到参数有3个,content就是你发送的内容,nonce是一个随机数,tts默认都是false我们可以不用管它

接着我们在频道内回复一条消息,然后继续查找控制台内的messages请求

post image

这次请求参数多了一个message_reference的对象,这个对象内有三个参数,分别是频道id,群id,回复的那条消息的id,知道这些参数后我们就可以构建我们的聊天函数

首先准备两个discord账号,获取好账号的authorization,以及需要聊天的群id和频道id,新建一个txt_list.txt的文本文件,把之前抓取到的话术整理好放入其中,一行就是一条消息,比如第一行由账号1发送,第二天就是由账号2发送,第三条是账号1发送这样循环

我们新建一个chat.js 下面是具体代码实现

const fs = require('fs');
const axios = require("axios");

const proxyHost = '127.0.0.1' // 代理ip
const proxyPort = '7890' // 代理端口号

const guild_id = '' // 群id
const channel_id = '' // 频道id
let message_id = '' // 回复的消息id
const account1 = '' // 账号1的authorization
const account2 = '' // 账号2的authorization

const time = 10 // 聊天间隔 单位:秒
const responeAt = 0.5 // 1 开启对话@ 0关闭对话@ 0.1-0.9 概率@
let counter = 0 // 计数器

// 从txt读取话术
const text_list = fs.readFileSync('text_list.txt').toString().split('\n');


/**
 * 开聊
 */
const chat_star = () => {
    // 用计数器判断当前使用账号1还是账号2发送消息
    let authorization = counter % 2 === 0 ? account1 : account2
    
    let header = {
        "Authorization": authorization,
        "Content-Type": "application/json",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36"
    }
    let msg = {
        "content": text_list[counter],
        "nonce": `94158662701${Math.ceil(Math.random()*1000)}213056`,
        "tts": false
    }
    if(isResponeAt() && !!message_id){
        msg.message_reference = {
            channel_id: channel_id,
            guild_id: guild_id,
            message_id: message_id
        }
    }
    axios({
        method: 'POST',
        url: `https://discord.com/api/v9/channels/${channel_id}/messages`,
        headers: header,
        proxy: (proxyHost && proxyPort)?{
            host: proxyHost,
            port: proxyPort
        }: null,
        data: msg
    }).then(res => {
        let data = res.data
        message_id = data.id
        console.log(`[${data.author.username}]发送成功: ${data.content}`)
        counter++
    })
}

/**
 * 是否为回复消息
 */
const isResponeAt = () => {
    if(responeAt === 1){
        return true
    }else if(responeAt === 0){
        return false
    }else{
        return Math.round(Math.random()*10) < responeAt*10
    }
}

const sleep = (ms) => {
    return new Promise((resolve) => setTimeout(resolve, ms));
};

const main = async () => {
    while (counter<text_list.length) {
        chat_star()
        await sleep(time*1000)
    }
}
main()

为了避免全程相互回复聊天别人发现异常,封装了一个判断当前是否为回复消息的函数 isResponseAt,通过配置参数responseAt来控制(1: 全程回复聊天 0:全程普通聊天 0.1-0.9:概率是回复消息,比如0.5就是每次发送消息都是二分之一的可能是回复消息)

其他就没啥了,功能其实还是挺简单的,我们在终端输入node chat.js就可以运行了,目前代码是判断text_list内的话术聊完就停止了,如果需要无限循环聊下去或者聊够指定条就停可以自己稍微改改就行,下面是设置0.5概率的运行效果

post image

第一篇就先写到这,项目代码稍后会打包到github

课程1地址

github上的代码 运行所需配置项统一放到.env文件里了,下载后把项目内的.env.example改为.env,在其中配置即可

post image

来自DFarm Club