init: 1.0.0
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/auth"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/user"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func GetTelegramConfig(ctx context.Context, svcCtx *svc.ServiceContext) (*types.TelegramConfig, error) {
|
||||
|
||||
data, err := svcCtx.AuthModel.FindOneByMethod(ctx, "telegram")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get Telegram config failed: %v", err.Error())
|
||||
}
|
||||
var telegramConfig auth.TelegramAuthConfig
|
||||
err = json.Unmarshal([]byte(data.Config), &telegramConfig)
|
||||
if err != nil {
|
||||
logger.WithContext(ctx).Error("unmarshal telegram config failed", logger.Field("error", err.Error()))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.TelegramConfig{
|
||||
TelegramBotToken: telegramConfig.BotToken,
|
||||
TelegramNotify: *data.Enabled,
|
||||
TelegramWebHookDomain: telegramConfig.WebHookDomain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ApiLink(ctx *gin.Context, svcCtx *svc.ServiceContext, method string) string {
|
||||
cfg, _ := GetTelegramConfig(ctx, svcCtx)
|
||||
return "https://api.telegram.org/bot" + cfg.TelegramBotToken + "/" + method
|
||||
}
|
||||
|
||||
func SendUserMessage(ctx *gin.Context, svcCtx *svc.ServiceContext, u user.User, text string, parseMode string) {
|
||||
req, _ := http.NewRequest("GET", ApiLink(ctx, svcCtx, "sendMessage"), nil)
|
||||
q := req.URL.Query()
|
||||
|
||||
userTelegramChatId, ok := findTelegram(&u)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
q.Add("chat_id", strconv.FormatInt(userTelegramChatId, 10))
|
||||
if parseMode == "markdown" {
|
||||
text = strings.ReplaceAll(text, "_", "\\_")
|
||||
}
|
||||
q.Add("text", text)
|
||||
q.Add("parse_mode", parseMode)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
_, _ = http.DefaultClient.Do(req)
|
||||
|
||||
}
|
||||
|
||||
func SendAdminMessage(ctx *gin.Context, svcCtx *svc.ServiceContext, text string, parseMode string) {
|
||||
var adminTelegram []int64
|
||||
f := false
|
||||
adminTelegramJson, err := svcCtx.Redis.Get(ctx, "adminTelegram").Result()
|
||||
if err == nil {
|
||||
err = json.Unmarshal([]byte(adminTelegramJson), &adminTelegram)
|
||||
if err == nil {
|
||||
f = true
|
||||
}
|
||||
}
|
||||
if !f {
|
||||
svcCtx.DB.Model(&user.User{}).Where("is_admin = true").Pluck("telegram", &adminTelegram)
|
||||
val, _ := json.Marshal(adminTelegram)
|
||||
_ = svcCtx.Redis.Set(ctx, "TelegramConfig", string(val), time.Duration(3600)*time.Second).Err()
|
||||
}
|
||||
req, _ := http.NewRequest("GET", ApiLink(ctx, svcCtx, "sendMessage"), nil)
|
||||
q := req.URL.Query()
|
||||
if parseMode == "markdown" {
|
||||
text = strings.ReplaceAll(text, "_", "\\_")
|
||||
}
|
||||
q.Add("text", text)
|
||||
q.Add("parse_mode", parseMode)
|
||||
for _, telegram := range adminTelegram {
|
||||
q.Add("chat_id", strconv.FormatInt(telegram, 10))
|
||||
req.URL.RawQuery = q.Encode()
|
||||
_, _ = http.DefaultClient.Do(req)
|
||||
}
|
||||
}
|
||||
|
||||
func SetWebhook(ctx *gin.Context, svcCtx *svc.ServiceContext) error {
|
||||
configs, _ := svcCtx.SystemModel.GetSiteConfig(ctx)
|
||||
cfg := &types.SiteConfig{}
|
||||
tool.SystemConfigSliceReflectToStruct(configs, cfg)
|
||||
req, _ := http.NewRequest("GET", ApiLink(ctx, svcCtx, "setWebhook"), nil)
|
||||
q := req.URL.Query()
|
||||
q.Add("url", cfg.Host+"/telegram/webhook")
|
||||
req.URL.RawQuery = q.Encode()
|
||||
_, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "set webhook error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findTelegram(u *user.User) (int64, bool) {
|
||||
for _, item := range u.AuthMethods {
|
||||
if item.AuthType == "telegram" {
|
||||
// string to int64
|
||||
parseInt, err := strconv.ParseInt(item.AuthIdentifier, 10, 64)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return parseInt, true
|
||||
}
|
||||
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/perfect-panel/ppanel-server/internal/config"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/user"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type TelegramLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewTelegramLogic(ctx context.Context, svcCtx *svc.ServiceContext) *TelegramLogic {
|
||||
return &TelegramLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *TelegramLogic) TelegramLogic(req *tgbotapi.Update) {
|
||||
if req.Message != nil && req.Message.Text != "" {
|
||||
switch req.Message.Command() {
|
||||
case "traffic":
|
||||
if err := l.traffic(req.Message.Chat.ID); err != nil {
|
||||
l.Logger.Error("[TelegramLogic] Traffic Error: ", logger.Field("error", err.Error()), logger.Field("command", req.Message.Command()), logger.Field("chat_id", req.Message.Chat.ID))
|
||||
}
|
||||
case "bind":
|
||||
if err := l.bind(req.Message.Chat.ID, req.Message.CommandArguments()); err != nil {
|
||||
l.Logger.Error("[TelegramLogic] Bind Error: ", logger.Field("error", err.Error()), logger.Field("command", req.Message.Command()), logger.Field("chat_id", req.Message.Chat.ID))
|
||||
}
|
||||
case "start":
|
||||
if err := l.start(req); err != nil {
|
||||
l.Logger.Error("[TelegramLogic] Start Error: ", logger.Field("error", err.Error()), logger.Field("command", req.Message.Command()), logger.Field("chat_id", req.Message.Chat.ID), logger.Field("text", req.Message.Text))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
l.Logger.Error("[TelegramLogic] Message is empty")
|
||||
}
|
||||
}
|
||||
|
||||
func (l *TelegramLogic) sendMessage(bot *tgbotapi.BotAPI, message string, userId int64) error {
|
||||
msg := tgbotapi.NewMessage(userId, message)
|
||||
msg.ParseMode = "Markdown"
|
||||
_, err := bot.Send(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *TelegramLogic) traffic(userId int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *TelegramLogic) bind(userId int64, token string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *TelegramLogic) start(req *tgbotapi.Update) error {
|
||||
if req.Message.CommandArguments() == "" {
|
||||
return l.sendMessage(l.svcCtx.TelegramBot, "Please bind account!", req.Message.Chat.ID)
|
||||
} else {
|
||||
sessionId := req.Message.CommandArguments()
|
||||
// get session id from redis
|
||||
sessionIdCacheKey := fmt.Sprintf("%v:%v", config.SessionIdKey, sessionId)
|
||||
value, err := l.svcCtx.Redis.Get(context.Background(), sessionIdCacheKey).Result()
|
||||
if err != nil && !errors.Is(err, redis.Nil) {
|
||||
l.Errorw("TelegramLogic start Redis Get Error: ", logger.Field("error", err.Error()), logger.Field("session", sessionId))
|
||||
return l.sendMessage(l.svcCtx.TelegramBot, "Bind failed!", req.Message.Chat.ID)
|
||||
}
|
||||
if value == "" {
|
||||
l.Errorw("TelegramLogic start Redis Get Error: ", logger.Field("error", "session not found"), logger.Field("session", sessionId))
|
||||
return l.sendMessage(l.svcCtx.TelegramBot, "Bind failed!", req.Message.Chat.ID)
|
||||
}
|
||||
userId, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
l.Errorw("TelegramLogic start ParseInt Error: ", logger.Field("error", err.Error()), logger.Field("session", sessionId))
|
||||
return l.sendMessage(l.svcCtx.TelegramBot, "Bind failed!", req.Message.Chat.ID)
|
||||
}
|
||||
|
||||
method, err := l.svcCtx.UserModel.FindUserAuthMethodByPlatform(l.ctx, userId, "telegram")
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("TelegramLogic start FindUserAuthMethodByPlatform Error: ", logger.Field("error", err.Error()), logger.Field("userId", userId))
|
||||
return l.sendMessage(l.svcCtx.TelegramBot, "Bind failed!", req.Message.Chat.ID)
|
||||
}
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if err := l.svcCtx.UserModel.InsertUserAuthMethods(l.ctx, &user.AuthMethods{
|
||||
UserId: userId,
|
||||
AuthType: "telegram",
|
||||
AuthIdentifier: strconv.FormatInt(req.Message.Chat.ID, 10),
|
||||
Verified: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}); err != nil {
|
||||
l.Errorw("TelegramLogic start InsertUserAuthMethod Error: ", logger.Field("error", err.Error()), logger.Field("userId", userId))
|
||||
return l.sendMessage(l.svcCtx.TelegramBot, "Bind failed!", req.Message.Chat.ID)
|
||||
}
|
||||
} else {
|
||||
method.AuthIdentifier = strconv.FormatInt(req.Message.Chat.ID, 10)
|
||||
if err := l.svcCtx.UserModel.InsertUserAuthMethods(l.ctx, method); err != nil {
|
||||
l.Errorw("TelegramLogic start UpdateUserAuthMethod Error: ", logger.Field("error", err.Error()), logger.Field("userId", userId))
|
||||
return l.sendMessage(l.svcCtx.TelegramBot, "Bind failed!", req.Message.Chat.ID)
|
||||
}
|
||||
}
|
||||
// update user info to redis
|
||||
err = l.svcCtx.UserModel.UpdateUserCache(l.ctx, &user.User{
|
||||
Id: userId,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "update user cache failed")
|
||||
}
|
||||
|
||||
text, err := tool.RenderTemplateToString(BindNotify, map[string]string{
|
||||
"Id": strconv.FormatInt(userId, 10),
|
||||
"Time": time.Now().Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "render template failed")
|
||||
}
|
||||
return l.sendMessage(l.svcCtx.TelegramBot, text, req.Message.Chat.ID)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package telegram
|
||||
|
||||
const BindNotify = `🤖 **尊敬的用户,您已成功绑定 Bot!**
|
||||
|
||||
**绑定账号**: {{.Id}}
|
||||
**绑定时间**: {{.Time}}
|
||||
|
||||
感谢您的支持!🎉
|
||||
您现在可以通过该 Bot 随时管理您的账户和服务。如有任何问题,请联系客服。💬
|
||||
`
|
||||
|
||||
const PurchaseNotify = `🎉 **尊敬的用户,您已成功购买服务!**
|
||||
|
||||
**订单编号**: {{.OrderNo}}
|
||||
**订阅名称**: {{.SubscribeName}}
|
||||
**订单金额**: **{{.OrderAmount}}**
|
||||
**到期时间**: {{.ExpireTime}}
|
||||
|
||||
感谢您的支持!💖
|
||||
您的服务已成功激活,随时为您提供高速、稳定、安全的网络体验。
|
||||
如有疑问,请联系客服,我们将竭诚为您服务!💬`
|
||||
|
||||
const RenewalNotify = `🎉 **尊敬的用户,您已成功续费服务!**
|
||||
|
||||
**订单编号**: {{.OrderNo}}
|
||||
**订阅名称**: {{.SubscribeName}}
|
||||
**订单金额**: **{{.OrderAmount}}**
|
||||
**到期时间**: {{.ExpireTime}}
|
||||
|
||||
感谢您的支持!💖
|
||||
您的服务已成功激活,随时为您提供高速、稳定、安全的网络体验。
|
||||
如有疑问,请联系客服,我们将竭诚为您服务!💬`
|
||||
|
||||
// RechargeNotify 充值通知
|
||||
const RechargeNotify = `💳 **尊敬的用户,您的账户充值已完成!**
|
||||
|
||||
💰 **充值金额**: {{.OrderAmount}}
|
||||
🏦 **充值方式**: _{{.PaymentMethod}}_
|
||||
⏰ **充值时间**: {{.Time}}
|
||||
📊 **当前账户余额**: **{{.Balance}}**
|
||||
|
||||
感谢您的支持!🎉
|
||||
余额可用于购买套餐或其他服务。
|
||||
如有疑问,请联系客服,我们将竭诚为您服务!💬`
|
||||
|
||||
// AdminOrderNotify 管理员订单通知
|
||||
const AdminOrderNotify = `
|
||||
📦 **订单通知**
|
||||
|
||||
🆔 **系统订单号**: {{.OrderNo}}
|
||||
🔖 **商户订单号**: {{.TradeNo}}
|
||||
👤 **用户账号**: {{.UserEmail}}
|
||||
💰 **订单金额**: **{{.OrderAmount}}**
|
||||
📋 **订单状态**: **{{.OrderStatus}}**
|
||||
📦 **订阅名称**: _{{.SubscribeName}}_
|
||||
⏰ **下单时间**: {{.OrderTime}}
|
||||
💳 **支付方式**: _{{.PaymentMethod}}_
|
||||
`
|
||||
|
||||
// AdminOrderDaily 管理员每日订单统计
|
||||
const AdminOrderDaily = `
|
||||
📊 **每日流水统计**
|
||||
|
||||
**统计日期**: {{.Date}}
|
||||
**总订单数**: **{{.Orders}}**
|
||||
**总成交金额**: **{{.Amount}}**
|
||||
|
||||
**按套餐分类:**
|
||||
{{.Subscribe}}
|
||||
|
||||
**按支付方式:**
|
||||
{{.Payment}}
|
||||
|
||||
**总览:**
|
||||
**当日退款数**: {{.RefundOrders}} 单,退款金额:**{{RefundAmount}}**
|
||||
**实际入账金额**: **{{ActualAmount}}**
|
||||
|
||||
**请注意**:
|
||||
以上数据为系统自动统计,仅供参考。如需详细数据或对账,请查看管理后台。
|
||||
`
|
||||
|
||||
// SubscribeExpireNotify 订阅到期通知
|
||||
const SubscribeExpireNotify = `尊敬的用户,您的订阅即将到期。
|
||||
|
||||
📦 **订阅名称**: _{{.SubscribeName}}_
|
||||
⏰ **到期时间**: {{.ExpiredAt}}
|
||||
💰 **续费金额**: **{{.RenewalAmount}}**
|
||||
|
||||
为确保服务不受影响,请尽快续费。
|
||||
如有疑问,请联系客服,我们将竭诚为您服务!💬`
|
||||
|
||||
// UnbindNotify 解绑通知
|
||||
const UnbindNotify = `🤖 尊敬的用户,您好!
|
||||
|
||||
您的账户已成功解绑:
|
||||
|
||||
**用户ID**:{{.Id}}
|
||||
**解绑时间**:{{.Time}}
|
||||
|
||||
解绑后,您将无法通过该Bot进行账户相关操作。
|
||||
如需重新绑定,请访问[绑定页面](#)完成操作。
|
||||
|
||||
如有任何疑问,请随时联系客服,我们将竭诚为您服务!
|
||||
感谢您的理解与支持!`
|
||||
|
||||
// ResetTrafficNotify 重置流量通知
|
||||
const ResetTrafficNotify = `📊 尊敬的用户,您好!
|
||||
|
||||
您的账户流量已成功重置:
|
||||
|
||||
**用户邮箱**:{{.Email}}
|
||||
**套餐名称**:{{.SubscribeName}}
|
||||
**重置时间**:{{.ResetTime}}
|
||||
**到期时间**:{{.ExpireTime}}
|
||||
|
||||
新的流量额度已生效,感谢您的支持!
|
||||
如有任何问题,请随时联系客服,我们将竭诚为您服务!💬`
|
||||
Reference in New Issue
Block a user