Update bot logic, Dockerfile and docker-compose.yml
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# Build stage
|
# Build stage
|
||||||
FROM golang:1.21-alpine AS builder
|
FROM golang:1.25-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
@@ -15,6 +15,7 @@ ENV TZ=Asia/Shanghai
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /app/go-helper .
|
COPY --from=builder /app/go-helper .
|
||||||
|
COPY image.png .
|
||||||
COPY .env.example .env
|
COPY .env.example .env
|
||||||
|
|
||||||
ENTRYPOINT ["./go-helper"]
|
ENTRYPOINT ["./go-helper"]
|
||||||
|
|||||||
36
docker-compose.yml
Normal file
36
docker-compose.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
# PostgreSQL 数据库
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: gpt-team-db
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: teamhelper
|
||||||
|
volumes:
|
||||||
|
- pgdata:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# Go Helper Bot
|
||||||
|
bot:
|
||||||
|
build: .
|
||||||
|
container_name: gpt-team-bot
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
# 覆盖 .env 中的数据库连接串,指向 docker 内部的 db 服务
|
||||||
|
DATABASE_URL: postgres://postgres:postgres@db:5432/teamhelper?sslmode=disable
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pgdata:
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package bot
|
package bot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -589,17 +589,13 @@ func (b *Bot) callbackActionPick(chatID int64, msgID int, action string, userID
|
|||||||
}
|
}
|
||||||
|
|
||||||
backCallback = "cmd:list_accounts"
|
backCallback = "cmd:list_accounts"
|
||||||
prefix := "📧"
|
|
||||||
|
|
||||||
if action == "del" {
|
if action == "del" {
|
||||||
label = "🗑️ 选择要删除的账号"
|
label = "🗑️ 选择要删除的账号"
|
||||||
prefix = "🗑"
|
|
||||||
} else if action == "ref" {
|
} else if action == "ref" {
|
||||||
label = "🔄 选择要刷新的账号"
|
label = "🔄 选择要刷新的账号"
|
||||||
prefix = "🔄"
|
|
||||||
} else if action == "pending_invite" {
|
} else if action == "pending_invite" {
|
||||||
label = "📩 选择管理账户"
|
label = "📩 选择管理账户"
|
||||||
prefix = "📧"
|
|
||||||
backCallback = "cmd:back"
|
backCallback = "cmd:back"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,17 +618,29 @@ func (b *Bot) callbackActionPick(chatID int64, msgID int, action string, userID
|
|||||||
shown := accounts[start:end]
|
shown := accounts[start:end]
|
||||||
|
|
||||||
var rows [][]tgbotapi.InlineKeyboardButton
|
var rows [][]tgbotapi.InlineKeyboardButton
|
||||||
|
var lns []string
|
||||||
|
var rBtns []tgbotapi.InlineKeyboardButton
|
||||||
|
|
||||||
// Create a button map
|
for i, a := range shown {
|
||||||
for _, a := range shown {
|
|
||||||
btnAction := fmt.Sprintf("%s:%d", action, a.ID)
|
btnAction := fmt.Sprintf("%s:%d", action, a.ID)
|
||||||
|
idx := start + i + 1
|
||||||
|
|
||||||
|
lns = append(lns, fmt.Sprintf("*%d.* ID: `%d` (%s)", idx, a.ID, a.Email))
|
||||||
|
|
||||||
rows = append(rows, tgbotapi.NewInlineKeyboardRow(
|
rBtns = append(rBtns, tgbotapi.NewInlineKeyboardButtonData(
|
||||||
tgbotapi.NewInlineKeyboardButtonData(
|
fmt.Sprintf("%d", idx),
|
||||||
fmt.Sprintf("%s %s", prefix, a.Email),
|
btnAction,
|
||||||
btnAction,
|
|
||||||
),
|
|
||||||
))
|
))
|
||||||
|
|
||||||
|
// Row break every 5 items
|
||||||
|
if len(rBtns) == 5 {
|
||||||
|
rows = append(rows, tgbotapi.NewInlineKeyboardRow(rBtns...))
|
||||||
|
rBtns = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Append remaining buttons
|
||||||
|
if len(rBtns) > 0 {
|
||||||
|
rows = append(rows, tgbotapi.NewInlineKeyboardRow(rBtns...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pagination buttons
|
// Pagination buttons
|
||||||
@@ -647,9 +655,16 @@ func (b *Bot) callbackActionPick(chatID int64, msgID int, action string, userID
|
|||||||
rows = append(rows, tgbotapi.NewInlineKeyboardRow(navRow...))
|
rows = append(rows, tgbotapi.NewInlineKeyboardRow(navRow...))
|
||||||
}
|
}
|
||||||
|
|
||||||
rows = append(rows, tgbotapi.NewInlineKeyboardRow(
|
if action == "ref" {
|
||||||
tgbotapi.NewInlineKeyboardButtonData("⬅️ 返回", backCallback),
|
rows = append(rows, tgbotapi.NewInlineKeyboardRow(
|
||||||
))
|
tgbotapi.NewInlineKeyboardButtonData("🔄 刷新全部", "cmd:refresh_all"),
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData("⬅️ 返回", backCallback),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
rows = append(rows, tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData("⬅️ 返回", backCallback),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
kb := tgbotapi.InlineKeyboardMarkup{InlineKeyboard: rows}
|
kb := tgbotapi.InlineKeyboardMarkup{InlineKeyboard: rows}
|
||||||
|
|
||||||
@@ -658,7 +673,7 @@ func (b *Bot) callbackActionPick(chatID int64, msgID int, action string, userID
|
|||||||
pageInfo = fmt.Sprintf("\n(第 %d/%d 页)", page+1, totalPages)
|
pageInfo = fmt.Sprintf("\n(第 %d/%d 页)", page+1, totalPages)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.editMsgWithKeyboard(chatID, msgID, fmt.Sprintf("%s%s:", label, pageInfo), &kb)
|
b.editMsgWithKeyboard(chatID, msgID, fmt.Sprintf("%s%s:\n\n%s", label, pageInfo, strings.Join(lns, "\n")), &kb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) callbackDelAccount(chatID int64, msgID int, idStr string) {
|
func (b *Bot) callbackDelAccount(chatID int64, msgID int, idStr string) {
|
||||||
@@ -722,6 +737,16 @@ func (b *Bot) callbackRefAccount(chatID int64, msgID int, idStr string) {
|
|||||||
|
|
||||||
_ = b.db.UpdateAccountTokens(id, result.AccessToken, result.RefreshToken)
|
_ = b.db.UpdateAccountTokens(id, result.AccessToken, result.RefreshToken)
|
||||||
|
|
||||||
|
// Sync member counts after token refresh.
|
||||||
|
acc.Token = result.AccessToken
|
||||||
|
if userTotal, _, err2 := b.client.GetUsers(acc); err2 == nil {
|
||||||
|
invTotal := acc.InviteCount
|
||||||
|
if inv, _, err3 := b.client.GetInvites(acc); err3 == nil {
|
||||||
|
invTotal = inv
|
||||||
|
}
|
||||||
|
_ = b.db.UpdateAccountCounts(id, userTotal, invTotal)
|
||||||
|
}
|
||||||
|
|
||||||
kb := tgbotapi.NewInlineKeyboardMarkup(
|
kb := tgbotapi.NewInlineKeyboardMarkup(
|
||||||
tgbotapi.NewInlineKeyboardRow(
|
tgbotapi.NewInlineKeyboardRow(
|
||||||
tgbotapi.NewInlineKeyboardButtonData("⬅️ 返回列表", "cmd:list_accounts"),
|
tgbotapi.NewInlineKeyboardButtonData("⬅️ 返回列表", "cmd:list_accounts"),
|
||||||
@@ -1014,8 +1039,10 @@ func (b *Bot) handleMessage(msg *tgbotapi.Message) {
|
|||||||
if hasSess && !strings.HasPrefix(text, "/") {
|
if hasSess && !strings.HasPrefix(text, "/") {
|
||||||
switch sess.flowType {
|
switch sess.flowType {
|
||||||
case "redeem_code":
|
case "redeem_code":
|
||||||
|
b.deleteMsg(chatID, msg.MessageID)
|
||||||
b.handleRedeemCode(chatID, text)
|
b.handleRedeemCode(chatID, text)
|
||||||
case "redeem":
|
case "redeem":
|
||||||
|
b.deleteMsg(chatID, msg.MessageID)
|
||||||
b.handleRedeemEmail(chatID, sess, text)
|
b.handleRedeemEmail(chatID, sess, text)
|
||||||
case "login":
|
case "login":
|
||||||
b.handleLoginCallback(chatID, msg.MessageID, text)
|
b.handleLoginCallback(chatID, msg.MessageID, text)
|
||||||
@@ -1030,7 +1057,7 @@ func (b *Bot) handleMessage(msg *tgbotapi.Message) {
|
|||||||
if email == "" {
|
if email == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
accounts, err := b.db.GetOpenAccounts(b.cfg.TeamCapacity)
|
accounts, err := b.db.GetOpenAccounts(b.cfg.TeamCapacity - 1)
|
||||||
if err != nil || len(accounts) == 0 {
|
if err != nil || len(accounts) == 0 {
|
||||||
kb := tgbotapi.NewInlineKeyboardMarkup(
|
kb := tgbotapi.NewInlineKeyboardMarkup(
|
||||||
tgbotapi.NewInlineKeyboardRow(
|
tgbotapi.NewInlineKeyboardRow(
|
||||||
@@ -1230,7 +1257,7 @@ func (b *Bot) handleRedeemEmail(chatID int64, sess *chatSession, email string) {
|
|||||||
if panelMsgID != 0 {
|
if panelMsgID != 0 {
|
||||||
b.editMsgWithKeyboard(chatID, panelMsgID, "⏳ 正在处理兑换,请稍候...", nil)
|
b.editMsgWithKeyboard(chatID, panelMsgID, "⏳ 正在处理兑换,请稍候...", nil)
|
||||||
|
|
||||||
result, err := redeem.Redeem(b.db, b.client, sess.code, email, b.cfg.TeamCapacity)
|
result, err := redeem.Redeem(b.db, b.client, sess.code, email, b.cfg.TeamCapacity-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.editMsgWithKeyboard(chatID, panelMsgID, fmt.Sprintf("❌ 兑换失败: %s", err.Error()), &backKb)
|
b.editMsgWithKeyboard(chatID, panelMsgID, fmt.Sprintf("❌ 兑换失败: %s", err.Error()), &backKb)
|
||||||
return
|
return
|
||||||
@@ -1239,7 +1266,7 @@ func (b *Bot) handleRedeemEmail(chatID int64, sess *chatSession, email string) {
|
|||||||
} else {
|
} else {
|
||||||
msgID := b.sendAndGetID(chatID, "⏳ 正在处理兑换,请稍候...")
|
msgID := b.sendAndGetID(chatID, "⏳ 正在处理兑换,请稍候...")
|
||||||
|
|
||||||
result, err := redeem.Redeem(b.db, b.client, sess.code, email, b.cfg.TeamCapacity)
|
result, err := redeem.Redeem(b.db, b.client, sess.code, email, b.cfg.TeamCapacity-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.editMsg(chatID, msgID, fmt.Sprintf("❌ 兑换失败: %s", err.Error()))
|
b.editMsg(chatID, msgID, fmt.Sprintf("❌ 兑换失败: %s", err.Error()))
|
||||||
return
|
return
|
||||||
@@ -1337,9 +1364,9 @@ func (b *Bot) handleAddAccount(chatID int64, panelMsgID int, args string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate codes based on remaining capacity: 6 - (current user count + pending invites).
|
// Generate codes based on remaining capacity: (b.cfg.TeamCapacity - 1) - (current user count + pending invites).
|
||||||
newAcct, _ := b.db.GetAccountByID(id)
|
newAcct, _ := b.db.GetAccountByID(id)
|
||||||
codeCount := 6
|
codeCount := b.cfg.TeamCapacity
|
||||||
if newAcct != nil {
|
if newAcct != nil {
|
||||||
var userTotal, inviteTotal int
|
var userTotal, inviteTotal int
|
||||||
if ut, _, err2 := b.client.GetUsers(newAcct); err2 == nil {
|
if ut, _, err2 := b.client.GetUsers(newAcct); err2 == nil {
|
||||||
@@ -1348,7 +1375,7 @@ func (b *Bot) handleAddAccount(chatID int64, panelMsgID int, args string) {
|
|||||||
if it, _, err3 := b.client.GetInvites(newAcct); err3 == nil {
|
if it, _, err3 := b.client.GetInvites(newAcct); err3 == nil {
|
||||||
inviteTotal = it
|
inviteTotal = it
|
||||||
}
|
}
|
||||||
codeCount = 6 - (userTotal + inviteTotal)
|
codeCount = b.cfg.TeamCapacity - (userTotal + inviteTotal)
|
||||||
_ = b.db.UpdateAccountCounts(id, userTotal, inviteTotal)
|
_ = b.db.UpdateAccountCounts(id, userTotal, inviteTotal)
|
||||||
}
|
}
|
||||||
if codeCount < 0 {
|
if codeCount < 0 {
|
||||||
@@ -1549,7 +1576,7 @@ func (b *Bot) handleGenCodes(chatID int64, panelMsgID int, args string) {
|
|||||||
email := eligible[i].Email
|
email := eligible[i].Email
|
||||||
existing, _ := b.db.CountAvailableCodesByAccount(email)
|
existing, _ := b.db.CountAvailableCodesByAccount(email)
|
||||||
|
|
||||||
maxCodes := b.cfg.TeamCapacity - eligible[i].UserCount - eligible[i].InviteCount
|
maxCodes := (b.cfg.TeamCapacity - 1) - eligible[i].UserCount - eligible[i].InviteCount
|
||||||
if maxCodes < 0 {
|
if maxCodes < 0 {
|
||||||
maxCodes = 0
|
maxCodes = 0
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user