Skip to content

Commit 083441e

Browse files
committed
rewrote the bot to be self-hosted and use official API
1 parent 0de684b commit 083441e

10 files changed

+678
-317
lines changed

NotionService.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import os
2+
import requests
3+
import re
4+
import json
5+
6+
7+
class NotionService:
8+
BASE_API_URL = 'https://api.notion.com/v1/pages'
9+
10+
PAGE_TITLE_MAX_LENGTH = 40
11+
12+
def __init__(self) -> None:
13+
pass
14+
15+
def setup_settings(self) -> None:
16+
self.api_key: str = os.getenv('NOTION_API_KEY')
17+
self.page_url: str = os.getenv('NOTION_PAGE_URL')
18+
self.page_id: str = self.get_page_id_from_url(self.page_url)
19+
self.URL_HEADERS = {
20+
"Authorization": f"Bearer {self.api_key}",
21+
"Content-Type": "application/json",
22+
"Notion-Version": "2021-08-16"
23+
}
24+
25+
def create_page(self, text: str) -> None:
26+
headers = self.URL_HEADERS
27+
28+
payload = self.generate_create_page_request_body(text)
29+
response = requests.post(
30+
self.BASE_API_URL, headers=headers, data=json.dumps(payload))
31+
32+
if not response.ok:
33+
raise NotionServiceException(response.json())
34+
35+
def generate_create_page_request_body(self, text) -> object:
36+
title = text if len(
37+
text) < self.PAGE_TITLE_MAX_LENGTH else text[:self.PAGE_TITLE_MAX_LENGTH]+'...'
38+
return {
39+
"parent": {
40+
"page_id": self.page_id
41+
},
42+
"properties": {
43+
"title": {"title": [{"type": "text", "text": {"content": title}}]},
44+
45+
},
46+
"children": [
47+
{
48+
"object": "block",
49+
"type": "paragraph",
50+
"paragraph": {
51+
"text": [{"type": "text", "text": {"content": text}}]
52+
}
53+
}
54+
]
55+
}
56+
57+
def get_page_id_from_url(self, url: str) -> str:
58+
id_regex = re.compile(r'([\w|\d]{32}$)')
59+
id_raw = id_regex.findall(url)[0]
60+
id_processed = '-'.join([
61+
id_raw[0:8], id_raw[8:12], id_raw[12:16], id_raw[16:20], id_raw[20:]])
62+
return id_processed
63+
64+
65+
class NotionServiceException(Exception):
66+
pass

Pipfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ verify_ssl = true
55

66
[dev-packages]
77
flake8 = "*"
8+
pylint = "*"
9+
autopep8 = "*"
810

911
[packages]
1012
pytelegrambotapi = "*"
1113
notion = "*"
1214
requests = "==2.22.0"
1315
boto = "*"
1416
python-telegram-bot = "==12.0.0b1"
15-
psycopg2 = "*"
16-
sqlalchemy = "*"
1717

1818
[requires]
1919
python_version = "3.6"

Pipfile.lock

Lines changed: 464 additions & 145 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Procfile

Lines changed: 0 additions & 1 deletion
This file was deleted.

README.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# text-to-notion-bot
22

3-
A (really) simple Telegram bot for sending text into Notion, built with python-telegram-bot and notion.py.
4-
Here's how it works:
5-
1. Get your 'token_v2' from cookies while being on any of your Notion.so pages.
6-
2. Use /setclient command and pass token_v2's value to bot.
7-
3. Use /setpage command and pass URL of your Notion.so page to bot.
8-
4. You're set! Any text you send to bot afterwards will be appended to said Notion.so page.
3+
Really simple self-hosted single-user telegram bot for sending text into Notion, using the official API.
94

10-
Plans
11-
* ✅ Persistent user data via DB
12-
* Multiple Notion pages
13-
* Some kind of interface to choose pages
5+
1. Register telegram bot and get bot token by communicationg with https://t.me/botfather
6+
2. Create Notion Integration and get API key (https://developers.notion.com/docs/getting-started#step-1-create-an-integration)
7+
3. 'Share' any Notion page with your Notion Integration (https://developers.notion.com/docs/getting-started#step-2-share-a-database-with-your-integration)
8+
4. Set up environment variables:
9+
- BOT_TOKEN=your telegram bot token
10+
- TELEGRAM_USERNAME=your telegram username (will be used for authorization)
11+
- NOTION_API_KEY=Notion API key
12+
- NOTION_PAGE_URL=URL of the page you shared with the integration (bot will parse page ID from it)
13+
5. Run the bot.
14+
- Text sent to the bot will be added to Notion page specified in environment variable as child page.
15+
- Title and content will be equal to the text sent.
16+
- Long titles (>50 characters) will be truncated automatically

__main__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from Bot import Bot
2+
3+
bot = Bot()
4+
bot.run()

bot.py

Lines changed: 65 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,73 @@
1-
import logging
21
import os
3-
import db
4-
from helpers import start, help_msg, done
5-
from helpers import ask_notion_api_key, set_notion_api_key, setclient, check_client
6-
from helpers import askpage, set_page_address, connect_to_page, check_page, send_text_to_notion
7-
from helpers import TYPING_NOTION_API_KEY, TYPING_NOTION_PAGE_ADDRESS, keyboard
2+
import logging
83
from telegram.ext import Updater, CommandHandler, MessageHandler, ConversationHandler, Filters
9-
4+
from NotionService import NotionService
5+
from typing import Type
106

117
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
128
level=logging.INFO)
13-
149
logger = logging.getLogger(__name__)
1510

1611

17-
BOT_TOKEN = os.environ.get('BOT_TOKEN_NOTION')
18-
19-
def error(update, context):
20-
"""Log Errors caused by Updates."""
21-
logger.warning('Update "%s" caused error "%s"', update, error)
22-
23-
24-
def main():
25-
# pp = PicklePersistence(filename='notionbot')
26-
logger.info(f'bot token: {BOT_TOKEN}')
27-
updater = Updater(BOT_TOKEN, use_context=True)
28-
29-
dp = updater.dispatcher
30-
31-
convhandler = ConversationHandler(
32-
entry_points=[
33-
CommandHandler('start', start),
34-
CommandHandler('setclient', ask_notion_api_key),
35-
CommandHandler('setpage', askpage),
36-
],
37-
38-
states={
39-
TYPING_NOTION_API_KEY: [MessageHandler(Filters.text, set_notion_api_key)],
40-
TYPING_NOTION_PAGE_ADDRESS: [MessageHandler(Filters.text, set_page_address)],
41-
},
42-
43-
fallbacks=[CommandHandler('done', done)],
44-
name='my_conversation',
45-
persistent=False
46-
)
47-
48-
dp.add_handler(convhandler)
49-
50-
help_handler = CommandHandler('help', help_msg)
51-
dp.add_handler(help_handler)
52-
53-
check_client_handler = CommandHandler('check_client', check_client)
54-
dp.add_handler(check_client_handler)
55-
56-
check_page_handler = CommandHandler('check_page', check_page)
57-
dp.add_handler(check_page_handler)
58-
59-
send_text_to_notion_handler = MessageHandler(Filters.text, send_text_to_notion)
60-
dp.add_handler(send_text_to_notion_handler)
61-
62-
dp.add_error_handler(error)
63-
64-
updater.start_polling()
65-
66-
updater.idle()
67-
68-
69-
if __name__ == '__main__':
70-
main()
12+
class Bot:
13+
def __init__(self) -> None:
14+
pass
15+
16+
def run(self) -> None:
17+
self.setup()
18+
self.updater.start_polling()
19+
self.updater.idle()
20+
21+
def setup(self) -> None:
22+
try:
23+
self.BOT_TOKEN: str = os.getenv('BOT_TOKEN')
24+
self.USERNAME: str = os.getenv('TELEGRAM_USERNAME')
25+
self.notion: NotionService = NotionService()
26+
self.updater: Type[Updater] = Updater(
27+
self.BOT_TOKEN, use_context=True)
28+
self.dispatcher = self.updater.dispatcher
29+
self.notion.setup_settings()
30+
self.register_handlers()
31+
except Exception as e:
32+
raise BotException(e)
33+
34+
def register_handlers(self) -> None:
35+
self.dispatcher.add_handler(self.text_handler())
36+
self.dispatcher.add_handler(self.start_handler())
37+
self.dispatcher.add_handler(self.help_handler())
38+
self.dispatcher.add_error_handler(self.error)
39+
40+
def text_handler(self) -> Type[MessageHandler]:
41+
return MessageHandler(Filters.text, self.send_to_notion)
42+
43+
def send_to_notion(self, update, context):
44+
sender_username = update.message.from_user.username
45+
if sender_username == self.USERNAME:
46+
self.notion.create_page(update.message.text)
47+
logger.info('Successfully sent message to Notion')
48+
update.message.reply_text("Text sent to Notion.")
49+
else:
50+
update.message.reply_text(
51+
"⛔ Sorry, you are not authorized for this bot.")
52+
53+
def start_handler(self) -> Type[CommandHandler]:
54+
return CommandHandler('start', help)
55+
56+
def help_handler(self) -> Type[CommandHandler]:
57+
return CommandHandler('help', help)
58+
59+
def help(self, update, context):
60+
reply_text = '''
61+
1. Set up environment variables for BOT_TOKEN, USERNAME, NOTION_API_KEY, OTION_PAGE_URL https://github.com/dartungar/text-to-notion-bot
62+
2. Send text here
63+
3. Have a nice day!
64+
'''
65+
update.message.reply_text(reply_text)
66+
67+
def error(self, update, context):
68+
"""Log Errors caused by Updates."""
69+
logger.warning('Update "%s" caused error "%s"', update, error)
70+
71+
72+
class BotException(Exception):
73+
pass

db.py

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)