This post is also available in Chinese
Several years ago, when people started sending New Year’s greetings via WeChat, I experimented with a bot based on vbot. I wanted to send customized greetings to all friends whose alias wasn’t empty. However, after sending fewer than 10 messages, my WeChat web interface was banned. I helplessly tasted the tears of lacking technical skills and being blocked.
As a developer, why should we do manually what can be automated?
Project Description
A few days ago, I stumbled upon Wechaty while browsing, which rekindled my idea of creating a bot. This time, I wanted to make an entertainment-focused bot~
Game Features
This development includes a “Who is the Spy” mini-game. The room host cannot set game roles and the number of players—at least 4 participants are required. After each round, roles are reassigned, adding randomness and fun. [Scan the QR code at the bottom and send ding to automatically add friends and try it out~]
Assuming total players = ALL
Civilians [P]: Default is half of total players + 1, rounded down
Spies [W]: Default is random from 1 to total players - civilians
Blank [B]: ALL - P - W
Extra allocation: If W > 3, reassign by randomly reducing 0 or W - 2 people, adding reduced count to P
Therefore, typical player distributions are: 3/1/0, 3/1/1, 3/2/0, 4/1/1, 4/2/0....
Game rules:
Game ends when:
Surviving players / total players <= 1/2, i.e., 2/4, 2/5, 3/6, 3/7, 4/8
Victory conditions:
1. Civilians eliminate all spies and blanks → Civilians win
2. Spies survive until game end → Spies win
3. All spies are eliminated but blanks still survive → Blanks win
Here are some code snippets explaining the logic.
/**
* Define current round game role counts
* @param {Number} playLength [Total number of players]
* @return {Array} [Specific numbers for civilians, spies, and blanks]
*/
Word.defRoleNum = function (playLength) {
let Civilian, Undercover = 0
Civilian = parseInt(playLength / 2) + 1 // Half + 1
Undercover = randomNum(1, playLength - Civilian) // Random spy count
if (Undercover > 3) { // If spies > 3
const rand = randomNum(0, Undercover - 2)
Civilian += rand // Keep at least 1 spy, others go to civilians
Undercover -= rand
}
const Blank = playLength - Civilian - Undercover // Blank count
return [Civilian, Undercover, Blank];
}
// Define current round player roles
Word.defTurn = function (playsList) {
const playLength = playsList.length
if (playLength < 4) {
return
}
const roles = this.defRoleNum(playLength)
const roleList = deepClone(roles)
const randomWord = randomNum(1) ? this.randomWord() : this.randomWord().reverse()
// Using Chinese characters directly instead of conversion
let role = '平民';
let word = '';
let newPlaysList = []
for (var i = 0; i < playLength; i++) {
var index = randomNum(playsList.length - 1); // Random index
if (roles[2]) {
role = '白板';
word = '';
roles[2]--;
} else if (roles[1]) {
role = '卧底';
word = randomWord[1];
roles[1]--;
} else {
role = '平民';
word = randomWord[0];
}
playsList[index].role = role
playsList[index].word = word
// Sort by order
newPlaysList.push(playsList[index])
playsList.splice(index, 1); // Remove randomly selected element from arr
// console.log(roles)
}
// Sort
newPlaysList.sort((a, b) => {
return a.self_id - b.self_id
})
const first = randomNum(1, newPlaysList.length)
return { roleList, playsList: newPlaysList, first }
}
Project Usage
Directory Structure
configfolder contains common configuration files andflyiorequest-related configurationsimgsstores related imageslistenersstores bot initialization event handlers (modularized)gamesgame moduleiswho.jsWho is the Spy core module
on-friendship.jshandles friend requestson-login.jshandles loginon-message.jshandles user messages and group messageson-scan.jshandles login QR codeon-work.jsperforms additional tasks
scheduleencapsulates thenode-schedulelibrary for scheduled tasksmigrationsdatabase migration filesapistores all data requests and interface encapsulationsutilsencapsulation of common methodsapp.jsentry filedb.jsdatabase entry fileknexfile.jsdatabase configuration file
How to Use
- Modify
configconfiguration Open theconfig/index.jsfile and change the configuration to your own. token and name are required, appToken.tianxin is also required - Modify Tianxing interface configuration
Tianxing API website: https://tianapi.com/
After registering successfully, apply for the following interfaces: - Depends on sqlite3 and redis, redis needs to be installed separately
Then you can run it:
npm install
npm run initdb
npm start
Implemented Features
- Send group invitation keyword to automatically invite people to join the group
- Scenario mode
- Who is the Spy
- God Reply
- Daily English Quote
- Weather query
- Send keyword to kick people
Here are some interesting features, more to be added gradually:
- Random anonymous friend chat
- Life-oriented text adventure game
Who is the Spy Mini-Game

使用Wechaty开发微信群管理小助手