Skip to content

Commit

Permalink
Merge pull request #314 from soberhacker/develop
Browse files Browse the repository at this point in the history
4.0.0
  • Loading branch information
soberhacker authored Oct 30, 2024
2 parents 11c3826 + c180546 commit ea4992a
Show file tree
Hide file tree
Showing 17 changed files with 248 additions and 21 deletions.
1 change: 0 additions & 1 deletion .github/workflows/update-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ jobs:
pull-request-header: "Update version in CHANGELOG.md and package.json"
default-branch: develop
labels: automerge
last-release-sha: "59b7861318647436eced27dcfc98c2078da7d7ab"

- name: Print release outputs
env:
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## [4.0.0](https://github.com/soberhacker/obsidian-telegram-sync/compare/3.2.0...4.0.0) (2024-10-30)


### ⚠ BREAKING CHANGES

* add encryption by pin code

### Features

* add encryption by pin code ([791178d](https://github.com/soberhacker/obsidian-telegram-sync/commit/791178dbd89178d90b4a00a32238ac77f2249a7c))
* add encryption for the bot token ([f404ac5](https://github.com/soberhacker/obsidian-telegram-sync/commit/f404ac5b49f11f7318e42b6a3099d4b116695e2f))


### Bug Fixes

* missing parsing hashtags containing cyrillic symbols ([bbaf6ce](https://github.com/soberhacker/obsidian-telegram-sync/commit/bbaf6cee956664e68108c50fe3356fc4fe1602b4))

## [3.2.0](https://github.com/soberhacker/obsidian-telegram-sync/compare/3.1.0...3.2.0) (2024-09-25)


Expand Down
30 changes: 30 additions & 0 deletions docs/Bot Token Encryption.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#### PIN Code Bot Token Encryption

##### What type of encryption does the plugin use?

The plugin uses AES-256 encryption to store the bot token securely. By default, the token is encrypted and saved locally on your device. However, since the plugin is open-source, the encryption key is embedded in the code, making it theoretically possible for other plugins to access the encrypted token.

##### What additional protection does the PIN code provide?

Enabling PIN-based encryption means that the decryption process requires a user-defined PIN, which is not stored. The PIN exists only in the user’s memory, preventing other plugins from accessing it. This extra layer ensures that even if the encryption mechanism is known through the source code, only someone with the correct PIN can decrypt the bot token.

##### What risks does this encryption help prevent?

- **Misuse of Bot Token**: Prevents scenarios where a malicious plugin could extract the token and use it for unauthorized actions, such as sending spam or other undesired activities through your bot.
- **Bot Suspension**: Misuse of your bot token could lead to temporary or permanent suspension of your bot, making it unable to create new bots or send messages for a period (e.g., up to a month).

##### How does it work?

When this feature is enabled, you will be prompted to enter your PIN each time Obsidian starts. This PIN is used to decrypt the bot token for the session, keeping the token secure while stored on your device.

##### What to do if you forget your PIN?

If you forget your PIN, you will need to reset the encryption by re-entering your bot token in unencrypted form:

1. Open the plugin settings in Obsidian.
2. Enter your bot token without encryption.
3. Re-enable the encryption feature and set a new PIN.

##### Why is this important?

Given the open-source nature of the plugin, adding a user-defined PIN helps ensure that your bot token remains under your control, even if other plugins attempt to access it. This feature is crucial for maintaining the integrity of your bot and avoiding unintended suspensions or breaches.
6 changes: 2 additions & 4 deletions docs/Telegram Sync Insider Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
✅ processing messages older than 24 hours if Obsidian wasn't running<br/>
✅ easy installing the latest published beta versions<br/>
✅ plugin updates in the channel instead of informational messages in your bot<br/>
✅ processing messages older than 24 hours if Obsidian wasn't running<br/>
❌ sending notes from Telegram to Obsidian (_not implemented_)<br/>
❌ getting messages from other bots in connected group chats (_not implemented_)<br/>
❌ posting messages in selected chats (_not implemented_)<br/>
❌ processing messages from other bots in connected group chats (_not implemented_)<br/>
❌ posting notes as messages in selected Telegram chats (_not implemented_)<br/>

**⚠ For all of these features, you must connect both your Telegram user and bot.**
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "telegram-sync",
"name": "Telegram Sync",
"version": "3.2.0",
"version": "4.0.0",
"minAppVersion": "1.0.0",
"description": "Transfer messages and files from Telegram to Obsidian.",
"author": "soberhacker",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "obsidian-telegram-sync",
"version": "3.2.0",
"version": "4.0.0",
"description": "Transfer messages and files from Telegram bot to Obsidian.",
"main": "main.js",
"scripts": {
Expand Down
8 changes: 4 additions & 4 deletions release-notes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
// TODO NEXT: check reconnecting
// TODO NEXT: bur in reconnecting on MacBook https://t.me/sm1rnov_id
import { compareVersions } from "compare-versions";
export const releaseVersion = "3.2.0";
export const releaseVersion = "4.0.0";
export const showNewFeatures = true;
export let showBreakingChanges = true;

const newFeatures = `In this release, the main change is that all processed messages will be marked with the bot reaction [👾] instead of replying with a separate message [...✅...].`;
export const breakingChanges = `⚠️ <b><i>Breaking changes!\n\nThe user's connection to the plugin may need to be reestablished due to the GramJs library update.\n\nGrant your bot admin rights if you want to use bot reactions in groups and channels.</i></b> ⚠️`;
const newFeatures = `In this release, security has been enhanced by encrypting the bot token`;
export const breakingChanges = `⚠️ <b><i>Breaking changes!\n\nBot token may need to be re-entered.</i></b> ⚠️`;
export const telegramChannelLink = "https://t.me/obsidian_telegram_sync";
export const insiderFeaturesLink =
"https://github.com/soberhacker/obsidian-telegram-sync/blob/main/docs/Telegram%20Sync%20Insider%20Features.md";
const telegramChannelAHref = `<a href='${telegramChannelLink}'>channel</a>`;
const insiderFeaturesAHref = `<a href='${insiderFeaturesLink}'>insider features</a>`;
const telegramChannelIntroduction = `Subscribe for free to the plugin's ${telegramChannelAHref} and enjoy access to ${insiderFeaturesAHref} and the latest beta versions, several months ahead of public release.`;
const telegramChatLink = "<a href='https://t.me/tribute/app?startapp=sfFf'>chat</a>";
const telegramChatIntroduction = `Join the plugin's ${telegramChatLink} - your space to seek advice, ask questions, and share knowledge (access via the @tribute bot).`;
const telegramChatIntroduction = `Join the plugin's ${telegramChatLink} - your space to seek advice, ask questions, and share knowledge (access via the tribute bot).`;
const donation = `If you appreciate this plugin and would like to support its continued development, please consider donating through the buttons below or via Telegram Stars in the ${telegramChannelAHref}!`;
const bestRegards = "Best regards,\nYour soberhacker🍃🧘💻\n⌞";

Expand Down
31 changes: 31 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {
} from "./settings/messageDistribution";
import os from "os";
import { clearCachedUnprocessedMessages, forwardUnprocessedMessages } from "./telegram/user/sync";
import { decrypt, encrypt } from "./utils/crypto256";
import { PinCodeModal } from "./settings/modals/PinCode";

// TODO LOW: add "connecting"
export type ConnectionStatus = "connected" | "disconnected";
Expand Down Expand Up @@ -59,6 +61,7 @@ export default class TelegramSyncPlugin extends Plugin {
status: PluginStatus = "loading";
time4processOldMessages = false;
processOldMessagesIntervalId?: NodeJS.Timer;
pinCode?: string = undefined;

async initTelegram(initType?: Client.SessionType) {
this.lastPollingErrors = [];
Expand Down Expand Up @@ -250,6 +253,11 @@ export default class TelegramSyncPlugin extends Plugin {
});
}

if (!this.settings.botTokenEncrypted) {
this.botTokenEncrypt();
needToSaveSettings = true;
}

needToSaveSettings && (await this.saveSettings());
}

Expand All @@ -273,4 +281,27 @@ export default class TelegramSyncPlugin extends Plugin {
else if (!error) displayAndLog(this, StatusMessages.BOT_DISCONNECTED, 0);
else displayAndLogError(this, error, StatusMessages.BOT_DISCONNECTED, checkConnectionMessage, undefined, 0);
}

async getBotToken(): Promise<string> {
if (!this.settings.botTokenEncrypted) return this.settings.botToken;

if (this.settings.encryptionByPinCode && !this.pinCode) {
await new Promise((resolve) => {
const pinCodeModal = new PinCodeModal(this, true);
pinCodeModal.onClose = async () => {
if (!this.pinCode) displayAndLog(this, "Plugin Telegram Sync stopped. No pin code entered.");
resolve(undefined);
};
pinCodeModal.open();
});
}
return decrypt(this.settings.botToken, this.pinCode);
}

botTokenEncrypt(saveSettings = false) {
this.settings.botToken = encrypt(this.settings.botToken, this.pinCode);
this.settings.botTokenEncrypted = true;
saveSettings && this.saveSettings();
displayAndLog(this, "Bot token encrypted", 0);
}
}
4 changes: 4 additions & 0 deletions src/settings/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export interface RefreshValues {

export interface TelegramSyncSettings {
botToken: string;
encryptionByPinCode: boolean;
botTokenEncrypted: boolean;
deleteMessagesFromTelegram: boolean;
allowedChats: string[];
mainDeviceId: string;
Expand All @@ -72,6 +74,8 @@ export interface TelegramSyncSettings {

export const DEFAULT_SETTINGS: TelegramSyncSettings = {
botToken: "",
encryptionByPinCode: false,
botTokenEncrypted: false,
deleteMessagesFromTelegram: false,
allowedChats: [""],
mainDeviceId: "",
Expand Down
40 changes: 38 additions & 2 deletions src/settings/modals/BotSettings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Modal, Setting } from "obsidian";
import TelegramSyncPlugin from "src/main";
import { _5sec, displayAndLog } from "src/utils/logUtils";
import { PinCodeModal } from "./PinCode";

export const mainDeviceIdSettingName = "Main device id";

Expand All @@ -16,6 +17,7 @@ export class BotSettingsModal extends Modal {
this.addBotToken();
this.addAllowedChatsSetting();
this.addDeviceId();
this.addEncryptionByPinCode();
this.addFooterButtons();
}

Expand Down Expand Up @@ -46,16 +48,17 @@ export class BotSettingsModal extends Modal {
new Setting(this.botSettingsDiv)
.setName("Bot token (required)")
.setDesc("Enter your Telegram bot token.")
.addText((text) => {
.addText(async (text) => {
text.setPlaceholder("example: 6123456784:AAX9mXnFE2q9WahQ")
.setValue(this.plugin.settings.botToken)
.setValue(await this.plugin.getBotToken())
.onChange(async (value: string) => {
if (!value) {
text.inputEl.style.borderColor = "red";
text.inputEl.style.borderWidth = "2px";
text.inputEl.style.borderStyle = "solid";
}
this.plugin.settings.botToken = value;
this.plugin.settings.botTokenEncrypted = false;
});
});
}
Expand Down Expand Up @@ -126,13 +129,46 @@ export class BotSettingsModal extends Modal {
});
}

addEncryptionByPinCode() {
const botTokenSetting = new Setting(this.botSettingsDiv)
.setName("Bot token encryption using a PIN code")
.setDesc(
"Encrypt the bot token for enhanced security. When enabled, a PIN code is required at each Obsidian launch. ",
)
.addToggle((toggle) => {
toggle.setValue(this.plugin.settings.encryptionByPinCode);
toggle.onChange(async (value) => {
if (this.plugin.settings.botTokenEncrypted) {
this.plugin.settings.botToken = await this.plugin.getBotToken();
this.plugin.settings.botTokenEncrypted = false;
}
this.plugin.settings.encryptionByPinCode = value;
if (!value) {
this.plugin.pinCode = undefined;
return;
}
const pinCodeModal = new PinCodeModal(this.plugin, false);
pinCodeModal.onClose = async () => {
if (pinCodeModal.saved && this.plugin.pinCode) return;
this.plugin.settings.encryptionByPinCode = false;
};
pinCodeModal.open();
});
});
botTokenSetting.descEl.createEl("a", {
href: "https://github.com/soberhacker/obsidian-telegram-sync/blob/main/docs/Bot%20Token%20Encryption.md",
text: "What does this can prevent?",
});
}

addFooterButtons() {
this.botSettingsDiv.createEl("br");
const footerButtons = new Setting(this.contentEl.createDiv());
footerButtons.addButton((b) => {
b.setTooltip("Connect")
.setIcon("checkmark")
.onClick(async () => {
if (!this.plugin.settings.botTokenEncrypted) this.plugin.botTokenEncrypt();
await this.plugin.saveSettings();
this.saved = true;
this.close();
Expand Down
74 changes: 74 additions & 0 deletions src/settings/modals/PinCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Modal, Setting } from "obsidian";
import TelegramSyncPlugin from "src/main";
import { _5sec } from "src/utils/logUtils";

export class PinCodeModal extends Modal {
pinCodeDiv: HTMLDivElement;
saved = false;
constructor(
public plugin: TelegramSyncPlugin,
public decrypt = false,
) {
super(plugin.app);
}

async display() {
this.addHeader();
this.addPinCode();
this.addFooterButtons();
}

success = async () => {
this.saved = true;
this.close();
};

addHeader() {
this.contentEl.empty();
this.pinCodeDiv = this.contentEl.createDiv();
this.titleEl.setText("Telegram Sync: " + (this.decrypt ? "Decrypting" : "Encrypting") + " bot token");
}

addPinCode() {
new Setting(this.pinCodeDiv)
.setName("PIN code")
.setDesc("Enter your PIN code. Numbers and letters only.")
.addText((text) => {
text.setPlaceholder("example: 1234").onChange(async (value: string) => {
if (!value) {
text.inputEl.style.borderColor = "red";
text.inputEl.style.borderWidth = "2px";
text.inputEl.style.borderStyle = "solid";
}
this.plugin.pinCode = value;
});
text.inputEl.addEventListener("keydown", (event: KeyboardEvent) => {
if (!(event.key === "Enter")) return;
this.success.call(this);
});
});
}

addFooterButtons() {
this.pinCodeDiv.createEl("br");
const footerButtons = new Setting(this.contentEl.createDiv());
footerButtons.addButton((b) => {
b.setTooltip("Connect").setIcon("checkmark").onClick(this.success);
return b;
});
footerButtons.addExtraButton((b) => {
b.setIcon("cross")
.setTooltip("Cancel")
.onClick(async () => {
this.saved = false;
this.plugin.pinCode = undefined;
this.close();
});
return b;
});
}

onOpen() {
this.display();
}
}
4 changes: 2 additions & 2 deletions src/telegram/bot/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import TelegramSyncPlugin from "src/main";
import { _1sec, displayAndLog } from "src/utils/logUtils";
import { handleMessage } from "./message/handlers";
import { reconnect } from "../user/user";
import { enqueueByCondition } from "src/utils/queues";
import { enqueue, enqueueByCondition } from "src/utils/queues";

// Initialize the Telegram bot and set up message handling
export async function connect(plugin: TelegramSyncPlugin) {
Expand All @@ -18,7 +18,7 @@ export async function connect(plugin: TelegramSyncPlugin) {
return;
}
// Create a new bot instance and start polling
plugin.bot = new TelegramBot(plugin.settings.botToken);
plugin.bot = new TelegramBot(await enqueue(plugin, plugin.getBotToken));
const bot = plugin.bot;
// Set connected flag to false and log errors when a polling error occurs
bot.on("polling_error", async (error: unknown) => {
Expand Down
2 changes: 1 addition & 1 deletion src/telegram/bot/message/getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export function getHashtag(msg: TelegramBot.Message, num = 1, lookInCaptions = t
const text = (msg.text || "") + (lookInCaptions && msg.caption ? msg.caption : "");
if (!text) return "";

const hashtags = text.match(/#\w+/g) || [];
const hashtags = text.match(/#[\p{L}\p{N}_]+/gu) || [];
return hashtags[num - 1]?.replace("#", "") || "";
}

Expand Down
Loading

0 comments on commit ea4992a

Please sign in to comment.