Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Telegram bot config UI #1747

Merged
merged 12 commits into from
May 26, 2023
7 changes: 7 additions & 0 deletions api/system_setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package api
import (
"encoding/json"
"fmt"
"strings"

"golang.org/x/exp/slices"
)
Expand Down Expand Up @@ -186,6 +187,12 @@ func (upsert SystemSettingUpsert) Validate() error {
return fmt.Errorf(systemSettingUnmarshalError, settingName)
}

case SystemSettingTelegramRobotTokenName:
fragments := strings.Split(upsert.Value, ":")
if len(fragments) != 2 {
return fmt.Errorf(systemSettingUnmarshalError, settingName)
}

default:
return fmt.Errorf("invalid system setting name")
}
Expand Down
7 changes: 2 additions & 5 deletions api/user_setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package api
import (
"encoding/json"
"fmt"
"strconv"

"golang.org/x/exp/slices"
)
Expand Down Expand Up @@ -101,12 +102,8 @@ func (upsert UserSettingUpsert) Validate() error {
return fmt.Errorf("invalid user setting memo visibility value")
}
} else if upsert.Key == UserSettingTelegramUserIDKey {
telegramUserID := 0
err := json.Unmarshal([]byte(upsert.Value), &telegramUserID)
_, err := strconv.Atoi(upsert.Value)
if err != nil {
return fmt.Errorf("failed to unmarshal user setting telegram userid value")
}
if telegramUserID <= 0 {
return fmt.Errorf("invalid user setting telegram userid value")
}
} else {
Expand Down
2 changes: 2 additions & 0 deletions plugin/telegram/robot.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func NewRobotWithHandler(h Handler) *Robot {
}

const noTokenWait = 30 * time.Second
const errRetryWait = 10 * time.Second

// Start start an infinity call of getUpdates from Telegram, call r.MessageHandle while get new message updates.
func (r *Robot) Start(ctx context.Context) {
Expand All @@ -37,6 +38,7 @@ func (r *Robot) Start(ctx context.Context) {
}
if err != nil {
log.Warn("fail to telegram.GetUpdates", zap.Error(err))
time.Sleep(errRetryWait)
continue
}

Expand Down
42 changes: 41 additions & 1 deletion web/src/components/Settings/PreferencesSection.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Switch, Option, Select } from "@mui/joy";
import { Input, Button, Divider, Switch, Option, Select } from "@mui/joy";
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import React from "react";
import { useTranslation } from "react-i18next";
import { useGlobalStore, useUserStore } from "@/store/module";
Expand All @@ -13,13 +15,17 @@ const PreferencesSection = () => {
const userStore = useUserStore();
const { appearance, locale } = globalStore.state;
const { setting, localSetting } = userStore.state.user as User;
const [telegramUserId, setTelegramUserId] = useState<string>("");
const visibilitySelectorItems = VISIBILITY_SELECTOR_ITEMS.map((item) => {
return {
value: item.value,
text: t(`memo.visibility.${item.text.toLowerCase()}`),
};
});

useEffect(() => {
setTelegramUserId(setting.telegramUserId);
CorrectRoadH marked this conversation as resolved.
Show resolved Hide resolved
}, [setting]);
const dailyReviewTimeOffsetOptions: number[] = [...Array(24).keys()];

const handleLocaleSelectChange = async (locale: Locale) => {
Expand Down Expand Up @@ -49,6 +55,21 @@ const PreferencesSection = () => {
userStore.upsertLocalSetting({ ...localSetting, enableAutoCollapse: event.target.checked });
};

const handleSaveTelegramUserId = async () => {
try {
await userStore.upsertUserSetting("telegram-user-id", parseInt(telegramUserId));
toast.success(t("common.dialog.success"));
} catch (error) {
console.error(error);
toast.error(t("common.dialog.error"));
return;
}
};

const handleTelegramUserIdChanged = async (value: string) => {
setTelegramUserId(value);
};

return (
<div className="section-container preferences-section-container">
<p className="title-text">{t("common.basic")}</p>
Expand Down Expand Up @@ -118,6 +139,25 @@ const PreferencesSection = () => {
<span className="normal-text">{t("setting.preference-section.auto-collapse")}</span>
<Switch className="ml-2" checked={localSetting.enableAutoCollapse} onChange={handleAutoCollapseChanged} />
</label>

<Divider className="!mt-3 !my-4" />

<div className="form-label">
<div className="flex flex-row items-center">
<span className="text-sm mr-1">{t("setting.preference-section.telegram-user-id")}</span>
</div>
<Button onClick={handleSaveTelegramUserId}>{t("common.save")}</Button>
</div>
<Input
className="w-full"
sx={{
fontFamily: "monospace",
fontSize: "14px",
}}
value={telegramUserId}
onChange={(event) => handleTelegramUserIdChanged(event.target.value)}
placeholder={t("setting.preference-section.telegram-user-id-placeholder")}
/>
</div>
);
};
Expand Down
44 changes: 44 additions & 0 deletions web/src/components/Settings/SystemSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const SystemSection = () => {
disablePublicMemos: systemStatus.disablePublicMemos,
maxUploadSizeMiB: systemStatus.maxUploadSizeMiB,
});
const [telegramRobotToken, setTelegramRobotToken] = useState<string>("");
const [openAIConfig, setOpenAIConfig] = useState<OpenAIConfig>({
key: "",
host: "",
Expand All @@ -47,6 +48,11 @@ const SystemSection = () => {
if (openAIConfigSetting) {
setOpenAIConfig(JSON.parse(openAIConfigSetting.value));
}

const telegramRobotSetting = systemSettings.find((setting) => setting.name === "telegram-robot-token");
if (telegramRobotSetting) {
setTelegramRobotToken(telegramRobotSetting.value);
}
});
}, []);

Expand Down Expand Up @@ -126,6 +132,24 @@ const SystemSection = () => {
toast.success("OpenAI Config updated");
};

const handleTelegramRobotTokenChanged = (value: string) => {
setTelegramRobotToken(value);
};

const handleSaveTelegramRobotToken = async () => {
try {
await api.upsertSystemSetting({
name: "telegram-robot-token",
value: telegramRobotToken,
});
} catch (error) {
console.error(error);
toast.error(t("common.dialog.error"));
return;
}
toast.success("OpenAI Config updated");
};

const handleAdditionalStyleChanged = (value: string) => {
setState({
...state,
Expand Down Expand Up @@ -246,6 +270,26 @@ const SystemSection = () => {
/>
</div>
<Divider className="!mt-3 !my-4" />
<div className="form-label">
<div className="flex flex-row items-center">
<span className="text-sm mr-1">{t("setting.system-section.telegram-robot-token")}</span>
<HelpButton
hint={t("setting.system-section.telegram-robot-token-description")}
url="https://core.telegram.org/bots#how-do-i-create-a-bot"
/>
</div>
<Button onClick={handleSaveTelegramRobotToken}>{t("common.save")}</Button>
</div>
<Input
className="w-full"
sx={{
fontFamily: "monospace",
fontSize: "14px",
}}
placeholder={t("setting.system-section.telegram-robot-token-placeholder")}
value={telegramRobotToken}
onChange={(event) => handleTelegramRobotTokenChanged(event.target.value)}
/>
boojack marked this conversation as resolved.
Show resolved Hide resolved
<div className="form-label">
<div className="flex flex-row items-center">
<span className="text-sm mr-1">{t("setting.system-section.openai-api-key")}</span>
Expand Down
5 changes: 5 additions & 0 deletions web/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@
"editor-font-style": "Editor font style",
"mobile-editor-style": "Mobile editor style",
"default-memo-sort-option": "Memo display time",
"telegram-user-id": "Telegram UserID",
"telegram-user-id-placeholder": "Send any words to Your Telegram Robot to get",
"created_ts": "Created Time",
"updated_ts": "Updated Time",
"daily-review-time-offset": "Daily Review Time Offset",
Expand Down Expand Up @@ -256,6 +258,9 @@
"additional-script": "Additional script",
"additional-style-placeholder": "Additional CSS code",
"additional-script-placeholder": "Additional JavaScript code",
"telegram-robot-token": "Telegram Robot Token",
"telegram-robot-token-description": "Get from @BotFather of Telegram",
"telegram-robot-token-placeholder": "Your Telegram Robot token",
"openai-api-key": "OpenAI: API Key",
"openai-api-key-description": "Get API key",
"openai-api-key-placeholder": "Your OpenAI API Key",
Expand Down
5 changes: 5 additions & 0 deletions web/src/locales/zh-Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@
"enable-double-click": "开启双击编辑",
"enable-folding-memo": "开启折叠备忘录",
"mobile-editor-style": "移动端编辑器样式",
"telegram-user-id": "Telegram UserID",
"telegram-user-id-placeholder": "向Telegram机器人发送任意消息以获取",
"theme": "主题",
"updated_ts": "更新时间"
},
Expand Down Expand Up @@ -404,6 +406,9 @@
"database-file-size": "数据库文件大小",
"disable-public-memos": "禁用公共备忘录",
"ignore-version-upgrade": "忽略版本升级",
"telegram-robot-token": "Telegram 机器人 Token",
"telegram-robot-token-description": "从 Telegram 的 @BotFather 处获取",
"telegram-robot-token-placeholder": "Telegram 的机器人 Token",
"openai-api-host": "OpenAI:API Host",
"openai-api-host-placeholder": "默认:https://api.openai.com/",
"openai-api-key": "OpenAI:API 密钥",
Expand Down
1 change: 1 addition & 0 deletions web/src/store/module/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const defaultSetting: Setting = {
locale: "en",
appearance: getSystemColorScheme(),
memoVisibility: "PRIVATE",
telegramUserId: "",
};

const defaultLocalSetting: LocalSetting = {
Expand Down
1 change: 1 addition & 0 deletions web/src/types/modules/setting.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ interface Setting {
locale: Locale;
appearance: Appearance;
memoVisibility: Visibility;
telegramUserId: string;
}

interface LocalSetting {
Expand Down