feat: add telegram proxy support (#1764)

* Add support for reverse proxy of telegram API

* Add Telegram API proxy hint

---------

Co-authored-by: Athurg Feng <athurg@gooth.org>
This commit is contained in:
Athurg Gooth 2023-05-29 13:29:21 +08:00 committed by GitHub
parent beb4d8ccb9
commit ce64894abe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 16 deletions

View File

@ -182,6 +182,14 @@ func (upsert SystemSettingUpsert) Validate() error {
if upsert.Value == "" { if upsert.Value == "" {
return nil return nil
} }
// Robot Token with Reverse Proxy shoule like `http.../bot<token>`
if strings.HasPrefix(upsert.Value, "http") {
slashIndex := strings.LastIndexAny(upsert.Value, "/")
if strings.HasPrefix(upsert.Value[slashIndex:], "/bot") {
return nil
}
return fmt.Errorf("token start with `http` must end with `/bot<token>`")
}
fragments := strings.Split(upsert.Value, ":") fragments := strings.Split(upsert.Value, ":")
if len(fragments) != 2 { if len(fragments) != 2 {
return fmt.Errorf(systemSettingUnmarshalError, settingName) return fmt.Errorf(systemSettingUnmarshalError, settingName)

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"strings"
) )
// downloadFileId download file with fileID, return the filepath and blob. // downloadFileId download file with fileID, return the filepath and blob.
@ -23,13 +24,19 @@ func (r *Robot) downloadFileID(ctx context.Context, fileID string) (string, []by
// downloadFilepath download file with filepath, you can get filepath by calling GetFile. // downloadFilepath download file with filepath, you can get filepath by calling GetFile.
func (r *Robot) downloadFilepath(ctx context.Context, filePath string) ([]byte, error) { func (r *Robot) downloadFilepath(ctx context.Context, filePath string) ([]byte, error) {
token := r.handler.RobotToken(ctx) apiURL, err := r.apiURL(ctx)
if token == "" { if err != nil {
return nil, ErrNoToken return nil, err
} }
uri := "https://api.telegram.org/file/bot" + token + "/" + filePath idx := strings.LastIndex(apiURL, "/bot")
resp, err := http.Get(uri) if idx < 0 {
return nil, ErrInvalidToken
}
fileURL := apiURL[:idx] + "/file" + apiURL[idx:]
resp, err := http.Get(fileURL + "/" + filePath)
if err != nil { if err != nil {
return nil, fmt.Errorf("fail to http.Get: %s", err) return nil, fmt.Errorf("fail to http.Get: %s", err)
} }

View File

@ -3,23 +3,19 @@ package telegram
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
) )
var ErrNoToken = errors.New("token is empty")
func (r *Robot) postForm(ctx context.Context, apiPath string, formData url.Values, result any) error { func (r *Robot) postForm(ctx context.Context, apiPath string, formData url.Values, result any) error {
token := r.handler.RobotToken(ctx) apiURL, err := r.apiURL(ctx)
if token == "" { if err != nil {
return ErrNoToken return err
} }
uri := "https://api.telegram.org/bot" + token + apiPath resp, err := http.PostForm(apiURL+apiPath, formData)
resp, err := http.PostForm(uri, formData)
if err != nil { if err != nil {
return fmt.Errorf("fail to http.PostForm: %s", err) return fmt.Errorf("fail to http.PostForm: %s", err)
} }

View File

@ -2,7 +2,9 @@ package telegram
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strings"
"time" "time"
"github.com/usememos/memos/common/log" "github.com/usememos/memos/common/log"
@ -32,7 +34,7 @@ func (r *Robot) Start(ctx context.Context) {
for { for {
updates, err := r.GetUpdates(ctx, offset) updates, err := r.GetUpdates(ctx, offset)
if err == ErrNoToken { if err == ErrInvalidToken {
time.Sleep(noTokenWait) time.Sleep(noTokenWait)
continue continue
} }
@ -79,3 +81,18 @@ func (r *Robot) Start(ctx context.Context) {
} }
} }
} }
var ErrInvalidToken = errors.New("token is invalid")
func (r *Robot) apiURL(ctx context.Context) (string, error) {
token := r.handler.RobotToken(ctx)
if token == "" {
return "", ErrInvalidToken
}
if strings.HasPrefix(token, "http") {
return token, nil
}
return "https://api.telegram.org/bot" + token, nil
}

View File

@ -293,7 +293,10 @@ const SystemSection = () => {
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<div className="w-auto flex items-center"> <div className="w-auto flex items-center">
<span className="text-sm mr-1">{t("setting.system-section.telegram-robot-token")}</span> <span className="text-sm mr-1">{t("setting.system-section.telegram-robot-token")}</span>
<HelpButton icon="help" url="https://usememos.com/docs/integration/telegram-bot" /> <HelpButton
hint={t("setting.system-section.telegram-robot-token-description")}
url="https://usememos.com/docs/integration/telegram-bot"
/>
</div> </div>
</div> </div>
<Button onClick={handleSaveTelegramRobotToken}>{t("common.save")}</Button> <Button onClick={handleSaveTelegramRobotToken}>{t("common.save")}</Button>

View File

@ -259,7 +259,7 @@
"additional-style-placeholder": "Additional CSS code", "additional-style-placeholder": "Additional CSS code",
"additional-script-placeholder": "Additional JavaScript code", "additional-script-placeholder": "Additional JavaScript code",
"telegram-robot-token": "Telegram Robot Token", "telegram-robot-token": "Telegram Robot Token",
"telegram-robot-token-description": "Get from @BotFather of Telegram", "telegram-robot-token-description": "Telegram Robot Token or API Proxy like `http.../bot<token>`",
"telegram-robot-token-placeholder": "Your Telegram Robot token", "telegram-robot-token-placeholder": "Your Telegram Robot token",
"openai-api-key": "OpenAI: API Key", "openai-api-key": "OpenAI: API Key",
"openai-api-key-description": "Get API key", "openai-api-key-description": "Get API key",

View File

@ -412,6 +412,7 @@
"ignore-version-upgrade": "忽略版本升级", "ignore-version-upgrade": "忽略版本升级",
"telegram-robot-token": "Telegram 机器人 Token", "telegram-robot-token": "Telegram 机器人 Token",
"telegram-robot-token-description": "从 Telegram 的 @BotFather 处获取", "telegram-robot-token-description": "从 Telegram 的 @BotFather 处获取",
"telegram-robot-token-description": "Telegram 机器人Token或`http.../bot<token>`格式的代理地址",
"telegram-robot-token-placeholder": "Telegram 的机器人 Token", "telegram-robot-token-placeholder": "Telegram 的机器人 Token",
"openai-api-host": "OpenAIAPI Host", "openai-api-host": "OpenAIAPI Host",
"openai-api-host-placeholder": "默认https://api.openai.com/", "openai-api-host-placeholder": "默认https://api.openai.com/",