mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 19:36:36 +08:00
fix(bot): complete command flow coverage
This commit is contained in:
@@ -319,6 +319,40 @@ func TestBotAdminCommandsManageDevicePolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBotRegistrationCommandUsesOpenRegQuota(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repos, bot := newBotTestService(t)
|
||||
admin := &model.User{Username: "root", PasswordHash: "x", Role: "admin", IsActive: true}
|
||||
if err := repos.User.Create(ctx, admin); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repos.Setting.Set(ctx, SettingOpenRegEnabled, "false"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repos.DB.Create(&model.TelegramBinding{
|
||||
TelegramUserID: 9051,
|
||||
TelegramName: "@root",
|
||||
ChatID: 9051,
|
||||
UserID: admin.ID,
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
channel := &model.NotifyChannel{Name: "Telegram", Type: "telegram", Enabled: true, Config: `{"admin_user_ids":"9051"}`}
|
||||
msg := &TelegramMessage{From: TelegramUser{ID: 9051, Username: "root"}, Chat: TelegramChat{ID: 9051, Type: "private"}}
|
||||
|
||||
reply, err := bot.executeCommand(ctx, channel, msg, "/registration on 2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(reply.Text, "2 个名额") {
|
||||
t.Fatalf("expected quota feedback, got %q", reply.Text)
|
||||
}
|
||||
capacity := bot.loadCapacity(ctx)
|
||||
if !capacity.OpenRegOn || capacity.OpenRegLimit != 2 || capacity.OpenRegUsed != 0 {
|
||||
t.Fatalf("registration command should open quota-aware registration, got %+v", capacity)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBotUserCommandsAndAdminGate(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repos, bot := newBotTestService(t)
|
||||
@@ -373,3 +407,85 @@ func TestBotUserCommandsAndAdminGate(t *testing.T) {
|
||||
t.Fatal("device should be marked kicked")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBotRedeemRegisterRequiresAllowedTelegramUser(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, bot := newBotTestService(t)
|
||||
code, err := bot.generateCode(ctx, model.RegistrationCodeRegister, 30, 0, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
channel := &model.NotifyChannel{Name: "Telegram", Type: "telegram", Enabled: true, Config: `{"admin_user_ids":"9001"}`}
|
||||
msg := &TelegramMessage{From: TelegramUser{ID: 9201, Username: "outsider"}, Chat: TelegramChat{ID: 9201, Type: "private"}}
|
||||
|
||||
reply, err := bot.executeCommand(ctx, channel, msg, "/redeem_register "+code.Code)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(reply.Text, "不在管理员配置") {
|
||||
t.Fatalf("outsider should not redeem register code, got %q", reply.Text)
|
||||
}
|
||||
|
||||
channel.Config = `{"admin_user_ids":"9201"}`
|
||||
reply, err = bot.executeCommand(ctx, channel, msg, "/redeem_register "+code.Code)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(reply.Text, "兑换成功") {
|
||||
t.Fatalf("allowed user should redeem register code, got %q", reply.Text)
|
||||
}
|
||||
if binding := bot.telegramBinding(ctx, 9201); binding == nil {
|
||||
t.Fatal("redeemed account should be bound to telegram user")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBotAdminCodeAndUserCommands(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repos, bot := newBotTestService(t)
|
||||
admin := &model.User{Username: "root", PasswordHash: "x", Role: "admin", IsActive: true}
|
||||
user := &model.User{Username: "viewer", PasswordHash: "x", Role: "user", IsActive: true}
|
||||
if err := repos.User.Create(ctx, admin); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repos.User.Create(ctx, user); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repos.DB.Create(&model.TelegramBinding{
|
||||
TelegramUserID: 9301,
|
||||
TelegramName: "@root",
|
||||
ChatID: 9301,
|
||||
UserID: admin.ID,
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
channel := &model.NotifyChannel{Name: "Telegram", Type: "telegram", Enabled: true, Config: `{"admin_user_ids":"9301"}`}
|
||||
msg := &TelegramMessage{From: TelegramUser{ID: 9301, Username: "root"}, Chat: TelegramChat{ID: 9301, Type: "private"}}
|
||||
|
||||
reply, err := bot.executeCommand(ctx, channel, msg, "/gencode renew 90 7")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(reply.Text, "已生成续期码") {
|
||||
t.Fatalf("expected generated renew code, got %q", reply.Text)
|
||||
}
|
||||
|
||||
reply, err = bot.executeCommand(ctx, channel, msg, "/renew_user viewer 30")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(reply.Text, "viewer") {
|
||||
t.Fatalf("renew command should return user actions, got %q", reply.Text)
|
||||
}
|
||||
updated, _ := repos.User.FindByID(ctx, user.ID)
|
||||
if updated.ExpiredAt == nil || updated.ExpiredAt.Before(time.Now()) {
|
||||
t.Fatalf("renew_user should set future expiry, got %v", updated.ExpiredAt)
|
||||
}
|
||||
|
||||
reply, err = bot.executeCommand(ctx, channel, msg, "/delete_user viewer")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(reply.Text, "需要确认") {
|
||||
t.Fatalf("delete without confirm should be rejected, got %q", reply.Text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,4 +110,9 @@ func TestTelegramCommandFiltering(t *testing.T) {
|
||||
if telegramSupportedCommand("/签到") {
|
||||
t.Fatal("unrelated group bot command should not be handled")
|
||||
}
|
||||
for _, cmd := range []string{"/signin", "/redeem", "/gencode", "/users", "/renew_user", "/delete_user", "/cleanup_rule"} {
|
||||
if !telegramSupportedCommand(cmd) {
|
||||
t.Fatalf("%s should be supported so group slash commands get feedback", cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,6 +255,8 @@ func (s *TelegramBotService) executeCommand(ctx context.Context, channel *model.
|
||||
return s.cmdHideAdult(ctx, msg, args), nil
|
||||
case "/account", "/me":
|
||||
return s.replyAccount(ctx, msg), nil
|
||||
case "/signin", "/checkin":
|
||||
return s.replySignIn(ctx, msg), nil
|
||||
case "/devices":
|
||||
return s.replyDevices(ctx, msg), nil
|
||||
case "/kick":
|
||||
@@ -263,6 +265,12 @@ func (s *TelegramBotService) executeCommand(ctx context.Context, channel *model.
|
||||
return s.cmdSetName(ctx, msg, args), nil
|
||||
case "/setpass", "/passwd", "/password":
|
||||
return s.cmdSetPass(ctx, msg, args), nil
|
||||
case "/redeem":
|
||||
return s.cmdRedeem(ctx, channel, msg, args), nil
|
||||
case "/redeem_register":
|
||||
return s.cmdRedeemRegister(ctx, channel, msg, args), nil
|
||||
case "/redeem_renew":
|
||||
return s.cmdRedeemRenew(ctx, msg, args), nil
|
||||
case "/register", "/reg", "/signup":
|
||||
return s.cmdRegister(ctx, channel, msg, args), nil
|
||||
case "/registration", "/reg_switch", "/openreg":
|
||||
@@ -270,6 +278,31 @@ func (s *TelegramBotService) executeCommand(ctx context.Context, channel *model.
|
||||
return telegramCommandReply{Text: "此命令仅管理员可用。"}, nil
|
||||
}
|
||||
return s.cmdRegistrationToggle(ctx, args), nil
|
||||
case "/capacity":
|
||||
if !s.telegramUserIsAdmin(ctx, channel, msg.From.ID) {
|
||||
return telegramCommandReply{Text: "此命令仅管理员可用。"}, nil
|
||||
}
|
||||
return s.replyCapacity(ctx), nil
|
||||
case "/users":
|
||||
if !s.telegramUserIsAdmin(ctx, channel, msg.From.ID) {
|
||||
return telegramCommandReply{Text: "此命令仅管理员可用。"}, nil
|
||||
}
|
||||
return s.replyUserList(ctx), nil
|
||||
case "/gencode":
|
||||
if !s.telegramUserIsAdmin(ctx, channel, msg.From.ID) {
|
||||
return telegramCommandReply{Text: "此命令仅管理员可用。"}, nil
|
||||
}
|
||||
return s.cmdGenCode(ctx, msg, args), nil
|
||||
case "/renew_user":
|
||||
if !s.telegramUserIsAdmin(ctx, channel, msg.From.ID) {
|
||||
return telegramCommandReply{Text: "此命令仅管理员可用。"}, nil
|
||||
}
|
||||
return s.cmdUserRenew(ctx, args), nil
|
||||
case "/delete_user":
|
||||
if !s.telegramUserIsAdmin(ctx, channel, msg.From.ID) {
|
||||
return telegramCommandReply{Text: "此命令仅管理员可用。"}, nil
|
||||
}
|
||||
return s.cmdUserDelete(ctx, args), nil
|
||||
case "/devicepolicy", "/policy":
|
||||
if !s.telegramUserIsAdmin(ctx, channel, msg.From.ID) {
|
||||
return telegramCommandReply{Text: "此命令仅管理员可用。"}, nil
|
||||
@@ -352,8 +385,10 @@ func telegramCommandName(text string) string {
|
||||
func telegramSupportedCommand(cmd string) bool {
|
||||
switch cmd {
|
||||
case "/start", "/menu", "/cancel", "/help", "/hideadult", "/hide_adult", "/adult",
|
||||
"/account", "/me", "/devices", "/kick", "/setname", "/rename", "/setpass", "/passwd", "/password",
|
||||
"/account", "/me", "/signin", "/checkin", "/devices", "/kick", "/setname", "/rename", "/setpass", "/passwd", "/password",
|
||||
"/redeem", "/redeem_register", "/redeem_renew",
|
||||
"/register", "/reg", "/signup", "/registration", "/reg_switch", "/openreg",
|
||||
"/capacity", "/users", "/gencode", "/renew_user", "/delete_user",
|
||||
"/devicepolicy", "/policy", "/antishare", "/cleanup", "/cleanup_mode", "/cleanup_rule",
|
||||
"/ban", "/unban", "/status", "/search", "/downloads", "/stats":
|
||||
return true
|
||||
@@ -388,7 +423,7 @@ func (s *TelegramBotService) cmdStart(ctx context.Context, msg *TelegramMessage,
|
||||
}
|
||||
}
|
||||
hint := "如果没有账号,请联系管理员注册。"
|
||||
if s.registrationEnabled(ctx) {
|
||||
if s.openRegEnabled(ctx) {
|
||||
hint = "如果还没有账号,可直接注册:\n<code>/register 用户名 密码</code>\n或:<code>/register 用户名-密码</code>"
|
||||
}
|
||||
return telegramCommandReply{Text: "<b>欢迎使用 MediaStationGo</b>\n\n普通用户请先绑定账号:\n<code>/start 用户名 密码</code>\n或:<code>/start 用户名-密码</code>\n\n" + hint}
|
||||
@@ -476,27 +511,47 @@ func (s *TelegramBotService) cmdRegister(ctx context.Context, channel *model.Not
|
||||
}
|
||||
}
|
||||
|
||||
// cmdRegistrationToggle 处理管理员的 /registration on|off|status 命令。
|
||||
// cmdRegistrationToggle handles /registration and /openreg. It uses the same
|
||||
// quota-aware open-registration state as the inline Bot menu.
|
||||
func (s *TelegramBotService) cmdRegistrationToggle(ctx context.Context, args []string) telegramCommandReply {
|
||||
current := s.registrationEnabled(ctx)
|
||||
if len(args) == 0 || strings.EqualFold(strings.TrimSpace(args[0]), "status") {
|
||||
state := map[bool]string{true: "已开启", false: "已关闭"}[current]
|
||||
return telegramCommandReply{Text: fmt.Sprintf("普通用户 Bot 注册功能当前<b>%s</b>。\n\n开启:<code>/registration on</code>\n关闭:<code>/registration off</code>", state)}
|
||||
c := s.loadCapacity(ctx)
|
||||
state := "已关闭"
|
||||
if c.OpenRegOn {
|
||||
if c.OpenRegLimit > 0 {
|
||||
state = fmt.Sprintf("已开启(%d/%d 名额)", c.OpenRegUsed, c.OpenRegLimit)
|
||||
} else {
|
||||
state = "已开启(不限名额,受授权上限约束)"
|
||||
}
|
||||
}
|
||||
return telegramCommandReply{Text: fmt.Sprintf("普通用户 Bot 注册功能当前<b>%s</b>。\n剩余可注册:<b>%d</b> 人。\n\n开启:<code>/registration on 10</code>\n不限:<code>/registration on 0</code>\n关闭:<code>/registration off</code>", state, c.Remaining())}
|
||||
}
|
||||
var next bool
|
||||
switch strings.ToLower(strings.TrimSpace(args[0])) {
|
||||
case "on", "true", "1", "open", "enable", "enabled", "开启", "打开", "开":
|
||||
next = true
|
||||
limit := 0
|
||||
if len(args) > 1 {
|
||||
n, err := strconv.Atoi(strings.TrimSpace(args[1]))
|
||||
if err != nil || n < 0 {
|
||||
return telegramCommandReply{Text: "名额必须是非负整数,0 表示不限名额。"}
|
||||
}
|
||||
limit = n
|
||||
}
|
||||
if err := s.openRegistration(ctx, limit); err != nil {
|
||||
return telegramCommandReply{Text: "开启失败:" + err.Error()}
|
||||
}
|
||||
label := "不限名额"
|
||||
if limit > 0 {
|
||||
label = fmt.Sprintf("%d 个名额", limit)
|
||||
}
|
||||
return telegramCommandReply{Text: "普通用户 Bot 注册功能已开启:" + label + "。"}
|
||||
case "off", "false", "0", "close", "disable", "disabled", "关闭", "关":
|
||||
next = false
|
||||
if err := s.closeRegistration(ctx); err != nil {
|
||||
return telegramCommandReply{Text: "关闭失败:" + err.Error()}
|
||||
}
|
||||
return telegramCommandReply{Text: "普通用户 Bot 注册功能已关闭。"}
|
||||
default:
|
||||
return telegramCommandReply{Text: "参数无效,请使用 <code>/registration on</code> 或 <code>/registration off</code>。"}
|
||||
return telegramCommandReply{Text: "参数无效,请使用 <code>/registration on [名额]</code> 或 <code>/registration off</code>。"}
|
||||
}
|
||||
if err := s.setRegistrationEnabled(ctx, next); err != nil {
|
||||
return telegramCommandReply{Text: "更新失败:" + err.Error()}
|
||||
}
|
||||
state := map[bool]string{true: "已开启", false: "已关闭"}[next]
|
||||
return telegramCommandReply{Text: fmt.Sprintf("普通用户 Bot 注册功能<b>%s</b>。此设置与系统设置页同步。", state)}
|
||||
}
|
||||
|
||||
// cmdHelp 处理 /help 命令。
|
||||
@@ -504,17 +559,19 @@ func (s *TelegramBotService) cmdHelp(ctx context.Context, msg *TelegramMessage)
|
||||
channel := s.findChannelForMessage(ctx, msg)
|
||||
if !s.telegramUserIsAdmin(ctx, channel, msg.From.ID) {
|
||||
register := ""
|
||||
if s.registrationEnabled(ctx) {
|
||||
if s.openRegEnabled(ctx) {
|
||||
register = "<b>/register 用户名 密码</b> — 注册新账号\n"
|
||||
}
|
||||
return "<b>MediaStationGo 用户命令</b>\n\n" +
|
||||
register +
|
||||
"<b>/start 用户名 密码</b> — 绑定账号\n" +
|
||||
"<b>/account</b> — 查看账号状态\n" +
|
||||
"<b>/signin</b> — 签到\n" +
|
||||
"<b>/devices</b> — 查看登录设备\n" +
|
||||
"<b>/kick all|编号</b> — 踢下线设备\n" +
|
||||
"<b>/setname 新用户名</b> — 修改用户名\n" +
|
||||
"<b>/setpass 新密码</b> — 修改密码\n" +
|
||||
"<b>/redeem 兑换码</b> — 注册或续期兑换\n" +
|
||||
"<b>/hideadult on|off</b> — 隐藏或显示成人目录\n\n" +
|
||||
"系统状态、搜索、下载列表与统计命令仅管理员可用。"
|
||||
}
|
||||
@@ -522,9 +579,13 @@ func (s *TelegramBotService) cmdHelp(ctx context.Context, msg *TelegramMessage)
|
||||
"<b>/start</b> — 开始使用\n" +
|
||||
"<b>/help</b> — 帮助信息\n" +
|
||||
"<b>/account</b> / <b>/devices</b> / <b>/kick all|编号</b> — 用户自助设备管理\n" +
|
||||
"<b>/signin</b> / <b>/redeem 兑换码</b> — 签到与兑换\n" +
|
||||
"<b>/setname 新用户名</b> / <b>/setpass 新密码</b> — 用户自助改名改密\n" +
|
||||
"<b>/register 用户名 密码</b> — 注册新账号(需管理员开启)\n" +
|
||||
"<b>/registration on|off</b> — 开启/关闭普通用户注册(管理员)\n" +
|
||||
"<b>/registration on [名额]|off</b> — 开启/关闭普通用户注册(管理员)\n" +
|
||||
"<b>/capacity</b> / <b>/users</b> — 容量与用户管理(管理员)\n" +
|
||||
"<b>/gencode register|renew 天数 [有效天数]</b> — 生成兑换码(管理员)\n" +
|
||||
"<b>/renew_user 用户名 天数</b> / <b>/delete_user 用户名 confirm</b> — 续期/删除用户(管理员)\n" +
|
||||
"<b>/antishare on play=3 login=3 warn=2</b> — 防共享策略(管理员)\n" +
|
||||
"<b>/cleanup on|off|run</b> — 删号规则开关/巡检(管理员)\n" +
|
||||
"<b>/cleanup_mode any|all|count 2</b> — 保号模式(管理员)\n" +
|
||||
|
||||
@@ -274,6 +274,31 @@ func (s *TelegramBotService) cmdSetPass(ctx context.Context, msg *TelegramMessag
|
||||
return s.selfSetPass(ctx, msg, strings.Join(args, " "))
|
||||
}
|
||||
|
||||
func (s *TelegramBotService) cmdRedeem(ctx context.Context, channel *model.NotifyChannel, msg *TelegramMessage, args []string) telegramCommandReply {
|
||||
if len(args) == 0 {
|
||||
return telegramCommandReply{Text: "请发送:<code>/redeem 兑换码</code>\n未绑定账号时自动尝试注册码;已绑定账号时自动尝试续期码。"}
|
||||
}
|
||||
code := strings.Join(args, " ")
|
||||
if s.boundUser(ctx, msg.From.ID) == nil {
|
||||
return s.redeemRegisterFlow(ctx, channel, msg, code)
|
||||
}
|
||||
return s.redeemRenewFlow(ctx, msg, code)
|
||||
}
|
||||
|
||||
func (s *TelegramBotService) cmdRedeemRegister(ctx context.Context, channel *model.NotifyChannel, msg *TelegramMessage, args []string) telegramCommandReply {
|
||||
if len(args) == 0 {
|
||||
return telegramCommandReply{Text: "请发送:<code>/redeem_register 注册兑换码</code>"}
|
||||
}
|
||||
return s.redeemRegisterFlow(ctx, channel, msg, strings.Join(args, " "))
|
||||
}
|
||||
|
||||
func (s *TelegramBotService) cmdRedeemRenew(ctx context.Context, msg *TelegramMessage, args []string) telegramCommandReply {
|
||||
if len(args) == 0 {
|
||||
return telegramCommandReply{Text: "请发送:<code>/redeem_renew 续期兑换码</code>"}
|
||||
}
|
||||
return s.redeemRenewFlow(ctx, msg, strings.Join(args, " "))
|
||||
}
|
||||
|
||||
func (s *TelegramBotService) replyAccount(ctx context.Context, msg *TelegramMessage) telegramCommandReply {
|
||||
user := s.boundUser(ctx, msg.From.ID)
|
||||
if user == nil {
|
||||
@@ -387,6 +412,12 @@ func (s *TelegramBotService) selfSetPass(ctx context.Context, msg *TelegramMessa
|
||||
// ── 兑换码流程 ───────────────────────────────────────────────────────────────
|
||||
|
||||
func (s *TelegramBotService) redeemRegisterFlow(ctx context.Context, channel *model.NotifyChannel, msg *TelegramMessage, raw string) telegramCommandReply {
|
||||
if channel == nil {
|
||||
channel = s.findChannelForMessage(ctx, msg)
|
||||
}
|
||||
if !s.telegramUserCanBind(ctx, channel, msg.From.ID) {
|
||||
return telegramCommandReply{Text: "当前 Telegram 账号不在管理员配置的绑定群组/频道中,无法兑换注册账号。请先加入管理员配置的群组或频道;如果尚未配置,请联系管理员。"}
|
||||
}
|
||||
rc, errMsg := s.lookupRedeemableCode(ctx, raw, model.RegistrationCodeRegister)
|
||||
if rc == nil {
|
||||
return telegramCommandReply{Text: errMsg}
|
||||
@@ -518,6 +549,50 @@ func (s *TelegramBotService) replyGenCode(ctx context.Context, msg *TelegramMess
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TelegramBotService) cmdGenCode(ctx context.Context, msg *TelegramMessage, args []string) telegramCommandReply {
|
||||
if len(args) < 2 {
|
||||
return telegramCommandReply{Text: "用法:<code>/gencode register|renew 天数 [有效天数]</code>\n示例:<code>/gencode register 30</code>、<code>/gencode renew 90 7</code>"}
|
||||
}
|
||||
kind := strings.ToLower(strings.TrimSpace(args[0]))
|
||||
switch kind {
|
||||
case "reg", "register", "注册码":
|
||||
kind = model.RegistrationCodeRegister
|
||||
case "renew", "续期", "续期码":
|
||||
kind = model.RegistrationCodeRenew
|
||||
default:
|
||||
return telegramCommandReply{Text: "类型无效,只支持 register / renew。"}
|
||||
}
|
||||
days, err := strconv.Atoi(args[1])
|
||||
if err != nil || days < 0 {
|
||||
return telegramCommandReply{Text: "天数必须是非负整数,0 表示永久。"}
|
||||
}
|
||||
validDays := 0
|
||||
if len(args) > 2 {
|
||||
validDays, err = strconv.Atoi(args[2])
|
||||
if err != nil || validDays < 0 {
|
||||
return telegramCommandReply{Text: "有效天数必须是非负整数。"}
|
||||
}
|
||||
}
|
||||
createdBy := ""
|
||||
if u := s.boundUser(ctx, msg.From.ID); u != nil {
|
||||
createdBy = u.ID
|
||||
}
|
||||
code, err := s.generateCode(ctx, kind, days, validDays, createdBy)
|
||||
if err != nil {
|
||||
return telegramCommandReply{Text: "生成失败:" + err.Error()}
|
||||
}
|
||||
kindLabel := map[string]string{model.RegistrationCodeRegister: "注册码", model.RegistrationCodeRenew: "续期码"}[code.Kind]
|
||||
dur := "永久"
|
||||
if days > 0 {
|
||||
dur = fmt.Sprintf("%d 天", days)
|
||||
}
|
||||
valid := "长期有效"
|
||||
if validDays > 0 && code.ExpiresAt != nil {
|
||||
valid = "有效至 " + code.ExpiresAt.Format("2006-01-02 15:04")
|
||||
}
|
||||
return telegramCommandReply{Text: fmt.Sprintf("已生成%s(%s,%s):\n\n<code>%s</code>", kindLabel, dur, valid, code.Code)}
|
||||
}
|
||||
|
||||
func (s *TelegramBotService) replyUserList(ctx context.Context) telegramCommandReply {
|
||||
users, err := s.repo.User.List(ctx)
|
||||
if err != nil {
|
||||
@@ -622,6 +697,44 @@ func (s *TelegramBotService) replyUserRenew(ctx context.Context, payload string)
|
||||
return s.replyUserActions(ctx, parts[0])
|
||||
}
|
||||
|
||||
func (s *TelegramBotService) cmdUserRenew(ctx context.Context, args []string) telegramCommandReply {
|
||||
if len(args) < 2 {
|
||||
return telegramCommandReply{Text: "用法:<code>/renew_user 用户名 天数</code>,天数 0 表示永久。"}
|
||||
}
|
||||
user, _ := s.repo.User.FindByUsername(ctx, args[0])
|
||||
if user == nil {
|
||||
user, _ = s.repo.User.FindByID(ctx, args[0])
|
||||
}
|
||||
if user == nil {
|
||||
return telegramCommandReply{Text: "未找到用户。"}
|
||||
}
|
||||
days, err := strconv.Atoi(args[1])
|
||||
if err != nil || days < 0 {
|
||||
return telegramCommandReply{Text: "天数必须是非负整数。"}
|
||||
}
|
||||
if err := s.applyRenewal(ctx, user.ID, days); err != nil {
|
||||
return telegramCommandReply{Text: "续期失败:" + err.Error()}
|
||||
}
|
||||
return s.replyUserActions(ctx, user.ID)
|
||||
}
|
||||
|
||||
func (s *TelegramBotService) cmdUserDelete(ctx context.Context, args []string) telegramCommandReply {
|
||||
if len(args) == 0 {
|
||||
return telegramCommandReply{Text: "用法:<code>/delete_user 用户名 confirm</code>\n为避免误删,最后一个参数必须是 confirm。"}
|
||||
}
|
||||
if len(args) < 2 || !strings.EqualFold(args[len(args)-1], "confirm") {
|
||||
return telegramCommandReply{Text: "删除用户需要确认:<code>/delete_user 用户名 confirm</code>"}
|
||||
}
|
||||
user, _ := s.repo.User.FindByUsername(ctx, args[0])
|
||||
if user == nil {
|
||||
user, _ = s.repo.User.FindByID(ctx, args[0])
|
||||
}
|
||||
if user == nil {
|
||||
return telegramCommandReply{Text: "未找到用户。"}
|
||||
}
|
||||
return s.replyUserDelete(ctx, user.ID)
|
||||
}
|
||||
|
||||
// protectReason returns a non-empty message when a user must not be
|
||||
// disabled/deleted (admins and the default admin are protected).
|
||||
func (s *TelegramBotService) protectReason(ctx context.Context, userID string) string {
|
||||
|
||||
Reference in New Issue
Block a user