feat: enhance event handling and add scheduling capabilities
- Introduced a new scheduler to manage timed tasks within the event dispatcher. - Updated the dispatcher to support the new scheduler, allowing for improved event processing. - Enhanced action serialization in the OneBot11 adapter to convert message chains to the appropriate format. - Added new dependencies for cron scheduling and other indirect packages in go.mod and go.sum. - Improved logging for event publishing and handler matching, providing better insights during execution. - Refactored plugin loading to include scheduled job management.
This commit is contained in:
347
internal/plugins/welcome/welcome.go
Normal file
347
internal/plugins/welcome/welcome.go
Normal file
@@ -0,0 +1,347 @@
|
||||
package welcome
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"cellbot/internal/engine"
|
||||
"cellbot/internal/protocol"
|
||||
"cellbot/pkg/utils"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 监听群成员增加通知事件
|
||||
engine.OnNotice("group_increase").
|
||||
Priority(50). // 设置较高优先级,确保及时响应
|
||||
Handle(handleWelcomeEvent)
|
||||
}
|
||||
|
||||
// handleWelcomeEvent 处理群成员加入欢迎事件
|
||||
func handleWelcomeEvent(ctx context.Context, event protocol.Event, botManager *protocol.BotManager, logger *zap.Logger) error {
|
||||
logger.Info("Welcome event received",
|
||||
zap.String("event_type", string(event.GetType())),
|
||||
zap.String("detail_type", event.GetDetailType()),
|
||||
zap.String("sub_type", event.GetSubType()),
|
||||
zap.String("self_id", event.GetSelfID()))
|
||||
|
||||
// 注意:中间件已经过滤了 detail_type,这里可以简化检查
|
||||
data := event.GetData()
|
||||
logger.Debug("Event data", zap.Any("data", data))
|
||||
|
||||
// 获取群ID和用户ID
|
||||
groupID, ok := data["group_id"]
|
||||
if !ok {
|
||||
logger.Warn("Missing group_id in event data")
|
||||
return nil
|
||||
}
|
||||
|
||||
userID, ok := data["user_id"]
|
||||
if !ok {
|
||||
logger.Warn("Missing user_id in event data")
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Info("Processing welcome event",
|
||||
zap.Any("group_id", groupID),
|
||||
zap.Any("user_id", userID))
|
||||
|
||||
// 获取操作者ID(邀请者或审批者)
|
||||
var operatorID interface{}
|
||||
if opID, exists := data["operator_id"]; exists {
|
||||
operatorID = opID
|
||||
}
|
||||
|
||||
// 获取子类型(approve: 管理员同意, invite: 邀请)
|
||||
subType := event.GetSubType()
|
||||
|
||||
// 获取 bot 实例
|
||||
selfID := event.GetSelfID()
|
||||
bot, ok := botManager.Get(selfID)
|
||||
if !ok {
|
||||
bots := botManager.GetAll()
|
||||
if len(bots) == 0 {
|
||||
logger.Error("No bot instance available")
|
||||
return nil
|
||||
}
|
||||
bot = bots[0]
|
||||
}
|
||||
|
||||
// 构建欢迎消息链(使用HTML模板渲染图片)
|
||||
logger.Info("Building welcome message",
|
||||
zap.Any("user_id", userID),
|
||||
zap.Any("operator_id", operatorID),
|
||||
zap.String("sub_type", subType))
|
||||
|
||||
welcomeChain, err := buildWelcomeMessage(ctx, userID, operatorID, subType, logger)
|
||||
if err != nil {
|
||||
logger.Error("Failed to build welcome message",
|
||||
zap.Any("user_id", userID),
|
||||
zap.Any("operator_id", operatorID),
|
||||
zap.String("sub_type", subType),
|
||||
zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("Welcome message built successfully",
|
||||
zap.Int("chain_length", len(welcomeChain)))
|
||||
|
||||
// 发送群消息
|
||||
logger.Info("Sending welcome message",
|
||||
zap.Any("group_id", groupID),
|
||||
zap.Any("user_id", userID))
|
||||
|
||||
action := &protocol.BaseAction{
|
||||
Type: protocol.ActionTypeSendGroupMessage,
|
||||
Params: map[string]interface{}{
|
||||
"group_id": groupID,
|
||||
"message": welcomeChain,
|
||||
},
|
||||
}
|
||||
|
||||
result, err := bot.SendAction(ctx, action)
|
||||
if err != nil {
|
||||
logger.Error("Failed to send welcome message",
|
||||
zap.Any("group_id", groupID),
|
||||
zap.Any("user_id", userID),
|
||||
zap.Any("action_type", action.Type),
|
||||
zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("Welcome message sent successfully",
|
||||
zap.Any("group_id", groupID),
|
||||
zap.Any("user_id", userID),
|
||||
zap.Any("result", result))
|
||||
|
||||
logger.Info("Welcome message sent",
|
||||
zap.Any("group_id", groupID),
|
||||
zap.Any("user_id", userID),
|
||||
zap.String("sub_type", subType))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// welcomeTemplate HTML欢迎消息模板
|
||||
const welcomeTemplate = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 40px 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.container {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.welcome-icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.title {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 18px;
|
||||
color: #666;
|
||||
}
|
||||
.content {
|
||||
margin: 30px 0;
|
||||
}
|
||||
.user-info {
|
||||
background: #f8f9fa;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
.info-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.info-label {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
}
|
||||
.info-value {
|
||||
color: #212529;
|
||||
}
|
||||
.join-type {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
margin: 20px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
.tips {
|
||||
background: #e7f3ff;
|
||||
border-left: 4px solid #2196F3;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.tips-title {
|
||||
font-weight: 600;
|
||||
color: #1976D2;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.tips-list {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
.tips-list li {
|
||||
padding: 5px 0;
|
||||
color: #424242;
|
||||
}
|
||||
.tips-list li:before {
|
||||
content: "• ";
|
||||
color: #2196F3;
|
||||
font-weight: bold;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="welcome-icon">🎉</div>
|
||||
<div class="title">欢迎加入!</div>
|
||||
<div class="subtitle">Welcome to the Group</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="user-info">
|
||||
<div class="info-item">
|
||||
<span class="info-label">用户ID:</span>
|
||||
<span class="info-value">{{.UserID}}</span>
|
||||
</div>
|
||||
{{if .OperatorID}}
|
||||
<div class="info-item">
|
||||
<span class="info-label">{{.OperatorLabel}}:</span>
|
||||
<span class="info-value">{{.OperatorID}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if .JoinType}}
|
||||
<div class="join-type">
|
||||
{{.JoinType}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="tips">
|
||||
<div class="tips-title">💡 温馨提示</div>
|
||||
<ul class="tips-list">
|
||||
<li>请遵守群规,文明交流</li>
|
||||
<li>如有问题可以@管理员</li>
|
||||
<li>发送 /help 查看帮助</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
希望你在群里玩得开心!
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
// buildWelcomeMessage 构建欢迎消息链(使用HTML模板渲染图片)
|
||||
func buildWelcomeMessage(ctx context.Context, userID, operatorID interface{}, subType string, logger *zap.Logger) (protocol.MessageChain, error) {
|
||||
logger.Debug("Starting to build welcome message",
|
||||
zap.Any("user_id", userID),
|
||||
zap.Any("operator_id", operatorID),
|
||||
zap.String("sub_type", subType))
|
||||
|
||||
// 准备模板数据
|
||||
data := map[string]interface{}{
|
||||
"UserID": fmt.Sprintf("%v", userID),
|
||||
}
|
||||
logger.Debug("Template data prepared", zap.Any("data", data))
|
||||
|
||||
// 根据加入方式设置不同的信息
|
||||
switch subType {
|
||||
case "approve":
|
||||
data["JoinType"] = "✅ 管理员审批通过"
|
||||
if operatorID != nil {
|
||||
data["OperatorID"] = fmt.Sprintf("%v", operatorID)
|
||||
data["OperatorLabel"] = "审批管理员"
|
||||
}
|
||||
case "invite":
|
||||
data["JoinType"] = "👥 被邀请加入"
|
||||
if operatorID != nil {
|
||||
data["OperatorID"] = fmt.Sprintf("%v", operatorID)
|
||||
data["OperatorLabel"] = "邀请人"
|
||||
}
|
||||
default:
|
||||
data["JoinType"] = "🎊 加入群聊"
|
||||
}
|
||||
|
||||
// 配置截图选项
|
||||
opts := &utils.ScreenshotOptions{
|
||||
Width: 800,
|
||||
Height: 600,
|
||||
Timeout: 60 * time.Second, // 增加超时时间到60秒
|
||||
WaitTime: 3 * time.Second, // 增加等待时间,确保页面完全加载
|
||||
FullPage: false,
|
||||
Quality: 90,
|
||||
Logger: logger,
|
||||
}
|
||||
|
||||
// 渲染模板并截图
|
||||
logger.Info("Rendering template and taking screenshot",
|
||||
zap.Int("width", opts.Width),
|
||||
zap.Int("height", opts.Height),
|
||||
zap.Duration("timeout", opts.Timeout))
|
||||
|
||||
chain, err := utils.ScreenshotTemplateToMessageChain(ctx, welcomeTemplate, data, opts)
|
||||
if err != nil {
|
||||
logger.Error("Failed to render welcome template",
|
||||
zap.Any("data", data),
|
||||
zap.Error(err))
|
||||
return nil, fmt.Errorf("failed to render welcome template: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("Template rendered and screenshot taken successfully",
|
||||
zap.Int("chain_length", len(chain)))
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
Reference in New Issue
Block a user