前言
群抽奖是社群运营最高频的互动场景之一:新品发布、节日福利、周年庆典,几乎每个活跃微信群都需要它。但手动点名、截图抽签不仅效率低,还容易引发"结果不透明"的质疑。本文用 WechatApi 个人微信 HTTP API 作为底座,从零搭建一套完整的微信群抽奖机器人,覆盖指令触发→报名收集→防重入→随机开奖→@中奖人公告的全流程,并给出可直接参考的示例代码。
一、需求拆解:一个合格的微信群抽奖机器人要做什么
在动手写代码前,先把功能拆清楚。一个生产级抽奖机器人至少需要以下五个环节:
| 环节 | 触发方式 | 说明 |
|---|---|---|
| 发起抽奖 | 管理员发送指令 #抽奖 一等奖x1 二等奖x3 | 创建活动、设置奖池 |
| 报名参与 | 群成员回复 +1 或 报名 | 收集参与者 wxid,去重 |
| 实时播报 | 自动回复当前报名人数 | 营造氛围,确认报名成功 |
| 开奖 | 管理员发送 #开奖 | 加权随机,产出获奖名单 |
| 公告 | 机器人在群内 @ 所有中奖者 | 通知到位,结果透明 |
此外还需考虑防作弊(同一 wxid 只能报名一次)、活动状态管理(同一时刻同一群只有一个进行中的抽奖)以及超时自动关闭(防止活动僵尸化)。
二、技术选型:为什么选 WechatApi 做微信机器人开发
微信机器人开发的核心诉求是:稳定接收群消息 + 灵活发送消息。WechatApi 基于 iPad 协议实现个人微信的 HTTP 接口,主要优势如下:
- 消息回调推送:有新群消息时主动 POST 到你的 Webhook 地址,无需轮询,延迟极低。
- 发消息走标准 HTTP POST:请求体为 JSON,鉴权头统一用
VideosApi-token,参数含appId,结构简单。 - 支持 @ 成员:发送群消息时传入
ats字段即可 @ 指定 wxid,完美契合开奖公告场景。 - 群成员列表接口:可拉取群内全量成员 wxid + 昵称,便于展示"第 X 号报名"。
接下来所有示例代码均基于该接口规范编写。
三、消息监听与关键词触发设计
3.1 Webhook 回调结构
在控制台 newmanager.wechatapi.net 配置好回调地址后,每条群消息会以如下 JSON 推送到你的服务端:
json{
"type": "group_msg",
"appId": "wx_xxxxxxxx",
"fromWxId": "wxid_abc123",
"fromNickName": "张三",
"groupId": "xxxxxxxx@chatroom",
"content": "#抽奖 iPhone配件x1 优惠券x5",
"msgId": "msg_20240601_001"
}
3.2 指令路由
用一个简单的前缀匹配分发指令,避免正则过度复杂:
python# Python 示例:消息路由
def dispatch(msg: dict):
content = msg["content"].strip()
group_id = msg["groupId"]
sender_wxid = msg["fromWxId"]
if content.startswith("#抽奖"):
handle_create(msg, content)
elif content in ("+1", "报名", "参加"):
handle_signup(msg)
elif content == "#开奖" and is_admin(sender_wxid, group_id):
handle_draw(msg)
elif content == "#取消抽奖" and is_admin(sender_wxid, group_id):
handle_cancel(msg)
# 其余消息忽略,避免误触
关键设计原则:
#抽奖和#开奖只有管理员可触发,通过预配置的ADMIN_WXIDS集合判断。- 报名指令用多个同义词兜底(
+1/报名/参加),降低用户操作门槛。 - 同一群同一时间只允许一个进行中的活动,创建时先检查 Redis/内存中是否已有活跃活动。
四、报名收集与去重机制
4.1 活动数据模型
用 Python dataclass(或你熟悉的任何结构)描述一场抽奖活动:
pythonfrom dataclasses import dataclass, field
from typing import Dict, List
import time
@dataclass
class LotteryActivity:
group_id: str
prizes: Dict[str, int] # {"iPhone配件": 1, "优惠券": 5}
creator_wxid: str
create_time: float = field(default_factory=time.time)
expire_seconds: int = 1800 # 30 分钟后自动过期
participants: Dict[str, str] = field(default_factory=dict)
# key=wxid, value=昵称;dict 天然去重
status: str = "ongoing" # ongoing / drawn / cancelled
def is_expired(self) -> bool:
return time.time() - self.create_time > self.expire_seconds
def signup(self, wxid: str, nick: str) -> bool:
"""返回 True 表示新报名成功,False 表示已报名"""
if wxid in self.participants:
return False
self.participants[wxid] = nick
return True
4.2 报名处理逻辑
python# 全局活动仓库:group_id -> LotteryActivity
activities: Dict[str, LotteryActivity] = {}
def handle_signup(msg: dict):
group_id = msg["groupId"]
wxid = msg["fromWxId"]
nick = msg["fromNickName"]
activity = activities.get(group_id)
if not activity or activity.status != "ongoing":
return # 无进行中的活动,静默忽略
if activity.is_expired():
activity.status = "cancelled"
send_group_text(group_id, "⏰ 本次抽奖已超时自动关闭,欢迎下次参与!")
return
is_new = activity.signup(wxid, nick)
count = len(activity.participants)
if is_new:
send_group_text(
group_id,
f"✅ @{nick} 报名成功!你是第 {count} 位参与者,祝好运~"
)
else:
# 已报名则私聊提示,避免刷屏
send_group_text(group_id, f"@{nick} 你已经报名啦,无需重复操作。", ats=[wxid])
去重的核心是 dict 的 key 唯一性——同一 wxid 第二次 signup 直接返回 False,无需额外的 Set 维护。
五、开奖算法:公平随机与加权抽取
5.1 等概率随机(最常见场景)
pythonimport random
def handle_draw(msg: dict):
group_id = msg["groupId"]
activity = activities.get(group_id)
if not activity or activity.status != "ongoing":
send_group_text(group_id, "当前没有进行中的抽奖活动。")
return
pool = list(activity.participants.items()) # [(wxid, nick), ...]
if not pool:
send_group_text(group_id, "还没有人报名,无法开奖 😅")
return
random.shuffle(pool) # Fisher-Yates shuffle,各元素等概率
winners: Dict[str, List[tuple]] = {} # prize_name -> [(wxid, nick)]
used_wxids = set()
for prize_name, count in activity.prizes.items():
winners[prize_name] = []
for wxid, nick in pool:
if wxid not in used_wxids and len(winners[prize_name]) < count:
winners[prize_name].append((wxid, nick))
used_wxids.add(wxid)
activity.status = "drawn"
announce_winners(group_id, winners)
为什么用random.shuffle而非random.sample? 多奖项场景下需要跨奖项去重,先 shuffle 后逐个挑选逻辑更清晰,且同样满足等概率要求。
5.2 加权抽取(VIP 会员双倍中奖率)
如果需要对特定成员加权(如付费用户权重 ×2),可在报名时记录权重,开奖时扩展名单:
python# 报名时:普通用户权重 1,VIP 用户权重 2
weighted_pool = []
for wxid, nick in pool:
weight = 2 if wxid in vip_wxids else 1
weighted_pool.extend([(wxid, nick)] * weight)
random.shuffle(weighted_pool)
# 注意:去重逻辑不变,同一 wxid 中奖后从 used_wxids 过滤
六、开奖公告与 @ 中奖人
6.1 调用 WechatApi 发送群消息并 @ 成员
WechatApi 发送群消息的接口范式:
httpPOST /api/send-group-message
VideosApi-token: <your_token>
Content-Type: application/json
{
"appId": "wx_xxxxxxxx",
"groupId": "xxxxxxxx@chatroom",
"content": "🎉 恭喜以下成员中奖!\n一等奖:@李四\n二等奖:@王五 @赵六",
"ats": ["wxid_lisi", "wxid_wangwu", "wxid_zhaoliu"]
}
返回示例:
json{"ret": 200, "msg": "ok", "data": {"msgId": "msg_draw_001"}}
ats 字段传入被 @ 成员的 wxid 数组,微信客户端会收到带红点的 @ 提醒,确保中奖者不会错过通知。
6.2 公告组装逻辑
pythondef announce_winners(group_id: str, winners: Dict[str, List[tuple]]):
lines = ["🎊 **抽奖结果公布!** 🎊\n"]
all_winner_wxids = []
for prize_name, winner_list in winners.items():
if not winner_list:
lines.append(f"【{prize_name}】暂无足够参与者,本奖项流奖。")
continue
nicks = " ".join(f"@{nick}" for _, nick in winner_list)
lines.append(f"【{prize_name}】{nicks}")
all_winner_wxids.extend(wxid for wxid, _ in winner_list)
lines.append("\n感谢所有参与的朋友,下次活动不见不散~")
content = "\n".join(lines)
send_group_text(group_id, content, ats=all_winner_wxids)
通过 WechatApi 微信群管理机器人 能力,发出的公告会在所有成员的聊天界面精准显示中奖者昵称高亮,中奖者手机也会收到 @ 推送通知。
七、防作弊与稳定性设计
7.1 常见作弊场景及对策
| 作弊手段 | 对策 |
|---|---|
| 同一人反复报名 | dict key 唯一,天然幂等 |
| 非群成员参与 | 开奖前调群成员列表接口,剔除已退群者 |
| 机器账号批量报名 | 结合注册时间/好友数等维度设置黑名单 wxid |
| 管理员自己中奖 | 发起人 wxid 加入排除列表,或公示后人工审核 |
| 活动期间换昵称 | 以 wxid 为唯一标识,昵称仅用于展示 |
7.2 活动超时兜底
利用 APScheduler 或 Celery Beat 定时扫描过期活动:
python# 每 5 分钟扫描一次过期活动
def cleanup_expired():
for group_id, activity in list(activities.items()):
if activity.status == "ongoing" and activity.is_expired():
activity.status = "cancelled"
send_group_text(
group_id,
"⏰ 抽奖活动已超时自动关闭。如需重新发起,请管理员使用 #抽奖 指令。"
)
7.3 高并发报名处理
群成员同一秒内可能涌入数十条报名消息。建议:
- 将报名消息写入消息队列(Redis List / RabbitMQ),由单线程消费者串行处理,彻底避免并发写冲突。
- 回包给用户的确认消息可异步发送,不必等待入库完成。
小结
本文以 WechatApi 为接口底座,完整实现了微信群抽奖机器人的五大核心模块:
- Webhook 消息监听:接收群消息回调,按指令前缀路由处理逻辑。
- 报名收集与去重:dict key 天然去重,多义词兜底降低参与门槛。
- 公平随机开奖:Fisher-Yates shuffle 保证等概率,加权扩展支持差异化权益。
- @ 中奖人公告:
ats字段精准触达,中奖者手机实时收到推送。 - 防作弊与超时兜底:多维度覆盖常见作弊手段,活动状态机管理生命周期。
整套方案代码量不超过 400 行,部署在任意有公网地址的服务器即可运行。如果你的群运营场景更复杂(多群并行、分奖池、历史记录查询),在此基础上扩展数据库持久化即可平滑升级。欢迎访问 WechatApi 控制台 申请接口体验,也可查阅 开发文档 获取完整接口规范。
