reorganise plugins and add date arg to journal cmd

This commit is contained in:
James Ravenscroft 2022-11-06 07:14:33 +00:00
parent 24fa84dfef
commit 8d3e24f443
8 changed files with 344 additions and 69 deletions

57
poetry.lock generated
View File

@ -118,6 +118,17 @@ category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
[[package]]
name = "feedparser"
version = "6.0.10"
description = "Universal feed parser, handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
sgmllib3k = "*"
[[package]] [[package]]
name = "flask" name = "flask"
version = "2.1.1" version = "2.1.1"
@ -174,6 +185,29 @@ category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
[[package]]
name = "pendulum"
version = "2.1.2"
description = "Python datetimes made easy"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.dependencies]
python-dateutil = ">=2.6,<3.0"
pytzdata = ">=2020.1"
[[package]]
name = "python-dateutil"
version = "2.8.2"
description = "Extensions to the standard Python datetime module"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
[package.dependencies]
six = ">=1.5"
[[package]] [[package]]
name = "python-dotenv" name = "python-dotenv"
version = "0.20.0" version = "0.20.0"
@ -224,6 +258,14 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
[package.dependencies] [package.dependencies]
tzdata = {version = "*", markers = "python_version >= \"3.6\""} tzdata = {version = "*", markers = "python_version >= \"3.6\""}
[[package]]
name = "pytzdata"
version = "2020.1"
description = "The Olson timezone database for Python."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.27.1" version = "2.27.1"
@ -242,6 +284,14 @@ urllib3 = ">=1.21.1,<1.27"
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
[[package]]
name = "sgmllib3k"
version = "1.0.0"
description = "Py3k port of sgmllib."
category = "main"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "six" name = "six"
version = "1.16.0" version = "1.16.0"
@ -329,7 +379,7 @@ watchdog = ["watchdog"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "aaeab26d780c5079dc962e4c086cbde44389716a5c642c36bedfaeaea3fd7621" content-hash = "2e482c8851ca14e2a5a862af70863b9875e65584bbaf49d048fcc446091d92ad"
[metadata.files] [metadata.files]
apscheduler = [ apscheduler = [
@ -371,6 +421,7 @@ dokuwiki = [
{file = "dokuwiki-1.3.2-py3-none-any.whl", hash = "sha256:0720b9fef9cbcb01879b2723b65a5f5bef63a68e9bfc362b09b2e3d9ba7cdedb"}, {file = "dokuwiki-1.3.2-py3-none-any.whl", hash = "sha256:0720b9fef9cbcb01879b2723b65a5f5bef63a68e9bfc362b09b2e3d9ba7cdedb"},
{file = "dokuwiki-1.3.2.tar.gz", hash = "sha256:af01bf878da69d915bf15a003cd9ef0d364a706a87acf74d00c6bb337636d350"}, {file = "dokuwiki-1.3.2.tar.gz", hash = "sha256:af01bf878da69d915bf15a003cd9ef0d364a706a87acf74d00c6bb337636d350"},
] ]
feedparser = []
flask = [ flask = [
{file = "Flask-2.1.1-py3-none-any.whl", hash = "sha256:8a4cf32d904cf5621db9f0c9fbcd7efabf3003f22a04e4d0ce790c7137ec5264"}, {file = "Flask-2.1.1-py3-none-any.whl", hash = "sha256:8a4cf32d904cf5621db9f0c9fbcd7efabf3003f22a04e4d0ce790c7137ec5264"},
{file = "Flask-2.1.1.tar.gz", hash = "sha256:a8c9bd3e558ec99646d177a9739c41df1ded0629480b4c8d2975412f3c9519c8"}, {file = "Flask-2.1.1.tar.gz", hash = "sha256:a8c9bd3e558ec99646d177a9739c41df1ded0629480b4c8d2975412f3c9519c8"},
@ -429,6 +480,8 @@ markupsafe = [
{file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
{file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
] ]
pendulum = []
python-dateutil = []
python-dotenv = [ python-dotenv = [
{file = "python-dotenv-0.20.0.tar.gz", hash = "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f"}, {file = "python-dotenv-0.20.0.tar.gz", hash = "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f"},
{file = "python_dotenv-0.20.0-py3-none-any.whl", hash = "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938"}, {file = "python_dotenv-0.20.0-py3-none-any.whl", hash = "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938"},
@ -445,10 +498,12 @@ pytz-deprecation-shim = [
{file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"},
{file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"},
] ]
pytzdata = []
requests = [ requests = [
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
] ]
sgmllib3k = []
six = [ six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},

View File

@ -17,6 +17,8 @@ python-telegram-bot = "^13.11"
todoist-api-python = "^1.1.1" todoist-api-python = "^1.1.1"
python-dotenv = "^0.20.0" python-dotenv = "^0.20.0"
bs4 = "^0.0.1" bs4 = "^0.0.1"
feedparser = "^6.0.10"
pendulum = "^2.1.2"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]

View File

@ -11,7 +11,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 2,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@ -21,7 +21,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 3,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@ -31,10 +31,38 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": 6,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [] "source": [
"r = session.get(\"https://wiki.jamesravey.me/api/books\")"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 Microcosm\n",
"2 PhD\n",
"4 House Stuff\n",
"5 Personal Stuff\n",
"7 🌱 Digital Garden Seed Propagator\n",
"8 ML and Data Science\n",
"9 Software Engineering\n",
"10 Devices and Tech\n",
"11 Journal\n"
]
}
],
"source": [
"for book in r.json()['data']:\n",
" print(book['id'], book['name'])"
]
}, },
{ {
"cell_type": "code", "cell_type": "code",
@ -45,28 +73,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 25, "execution_count": 28,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"# Bookmarks\n",
"\n",
"\n",
"\n",
"[example](http://www.google.com)\n"
]
}
],
"source": [
"print(r.json()['markdown'])"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@ -75,18 +82,12 @@
"<Response [200]>" "<Response [200]>"
] ]
}, },
"execution_count": 26, "execution_count": 28,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
], ],
"source": [ "source": []
"r = session.get(\"https://wiki.jamesravey.me/api/pages/58\")\n",
"\n",
"session.put(\"https://wiki.jamesravey.me/api/pages/58\", json={\n",
" \"markdown\": r.json()['markdown'] + \"\\n\\n - [example](http://www.google.com)\"\n",
"})"
]
}, },
{ {
"cell_type": "code", "cell_type": "code",

View File

@ -1,7 +1,10 @@
from collections import defaultdict
import dotenv import dotenv
import os import os
import dokuwiki import io
import requests import tempfile
from datetime import datetime
from flask import Flask from flask import Flask
@ -10,11 +13,13 @@ from telegram import Update, PhotoSize, File, BotCommand
from telegram.ext import Updater, MessageHandler, CommandHandler, Filters, CallbackContext, ConversationHandler from telegram.ext import Updater, MessageHandler, CommandHandler, Filters, CallbackContext, ConversationHandler
app = Flask("rafael") app = Flask("rafael")
RAFAEL_UA = "RAFAEL/0.1" RAFAEL_UA = "RAFAEL/0.1"
from rafael.bookmarks import RafaelBookmarkPlugin from rafael.bookmarks import RafaelBookmarkPlugin
from rafael.journal import JournalPlugin
def handle(update: Update, context: CallbackContext): def handle(update: Update, context: CallbackContext):
@ -40,39 +45,7 @@ def handle(update: Update, context: CallbackContext):
update.message.reply_text("done m8") update.message.reply_text("done m8")
ADDING = 0
class JournalPlugin():
def init_convo(self, update: Update, context: CallbackContext):
update.message.reply_text("Add photos for your journal")
return ADDING
def handle_file(self, update: Update, context: CallbackContext):
update.message.reply_text("Got your update m8")
return ADDING
def done_handler(self, update: Update, context: CallbackContext):
update.message.reply_text("Great storing your images...")
return ConversationHandler.END
def invalid_handler(self, update: Update, context: CallbackContext):
update.message.reply_text("Sorry, I don't understand. Please upload photos or type 'Done' to exit")
def register(self, updater: Updater):
updater.dispatcher.add_handler(ConversationHandler(
entry_points=[
CommandHandler("journal", self.init_convo)
],
states={
ADDING: [MessageHandler(Filters.attachment, self.handle_file)]
},
fallbacks=[
MessageHandler(Filters.regex("^Done$"), self.done_handler),
MessageHandler(Filters.all, self.invalid_handler)
]
))
class RafaelBot: class RafaelBot:

154
src/rafael/journal.py Normal file
View File

@ -0,0 +1,154 @@
import os
import io
import pendulum
from datetime import datetime
from collections import defaultdict
from telegram import Update, PhotoSize, File, BotCommand
from telegram.ext import Updater, MessageHandler, CommandHandler, Filters, CallbackContext, ConversationHandler
from rafael.util.bookstack import BookstackClient, Page
SELECT_DATE, ADDING = range(2)
class JournalPlugin():
def init_convo(self, update: Update, context: CallbackContext):
print("Args:", context.args)
journal_date = datetime.now()
if len(context.args) > 0 :
try:
if context.args[0] == "yesterday":
journal_date = pendulum.yesterday()
elif context.args[0] == "tomorrow":
journal_date = pendulum.tomorrow()
else:
journal_date = pendulum.parse(context.args[0])
except:
update.message.reply_text("""If you're passing a journal date you must use YYYY/MM/DD
You can also use 'yesterday' or 'tomorrow'""")
return
self.api = BookstackClient(os.getenv('BOOKSTACK_URL'), os.getenv('BOOKSTACK_TOKEN_ID'), os.getenv('BOOKMARK_TOKEN_SECRET'))
chapter_name = journal_date.strftime("%B %Y")
page_name = journal_date.strftime("%Y-%m-%d")
chapters = self.api.get_chapters(filters={"name":chapter_name, "book_id": os.getenv('BOOKSTACK_JOURNAL_BOOK')})['data']
chapter = chapters[0]
pages = self.api.get_pages(filters={"name": page_name, "chapter_id": chapter['id']})['data']
if len(pages)<1:
print("Create Page")
page = self.api.create_page(Page(chapter_id=chapter['id'], name=page_name, markdown=f"Journal for {page_name}"))
context.chat_data['page_id'] = page['id']
print(page)
else:
print("Update Page")
context.chat_data['page_id'] = pages[0]['id']
print(f"Use page {context.chat_data['page_id']}")
update.message.reply_text("Journal Mode On: Add text and files")
return ADDING
def handle_md_update(self, update: Update, context: CallbackContext):
page = self.api.get_page(context.chat_data['page_id'])
content = page['markdown'] + f"\n\n----------\n\n{update.message.text_markdown}"
self.api.update_page(Page(id=page['id'], markdown=content))
update.message.reply_text(f"Added text to journal page {page['id']}")
def handle_file(self, update: Update, context: CallbackContext):
if update.message.document is not None:
file_meta = update.message.document.get_file()
file_name = update.message.document.file_name
elif update.message.effective_attachment:
versions = defaultdict(lambda:[])
biggest = 0
biggest_i = -1
for i, att in enumerate(update.message.effective_attachment):
if isinstance(att, PhotoSize):
if att.width > biggest:
biggest = att.width
biggest_i = i
file_meta = update.message.effective_attachment[i].get_file()
file_name = update.message.effective_attachment[i].get_file().file_id
buf = io.BytesIO(file_meta.download_as_bytearray())
r = self.api.add_attachment_from_upload(context.chat_data['page_id'], file_name, buf)
page = self.api.get_page(context.chat_data['page_id'])
content = page['markdown'] + "\n\n-------\n\n" + f"![{r['name']}]({self.api.wiki_url}/attachments/{r['id']}?open=true)"
self.api.update_page(Page(id=page['id'], markdown=content))
update.message.reply_text(f"Added file attachment id={r['id']} name={r['name']}")
return ADDING
def done_handler(self, update: Update, context: CallbackContext):
update.message.reply_text("Journal mode off")
return ConversationHandler.END
def invalid_handler(self, update: Update, context: CallbackContext):
update.message.reply_text("Sorry, I don't understand. Please upload photos or type 'Done' to exit")
def register(self, updater: Updater):
updater.dispatcher.add_handler(ConversationHandler(
entry_points=[
CommandHandler("journal", self.init_convo)
],
states={
ADDING: [
MessageHandler(Filters.attachment, self.handle_file),
MessageHandler(Filters.regex("^Done$"), self.done_handler),
MessageHandler(Filters.all, self.handle_md_update),
]
},
fallbacks=[
MessageHandler(Filters.regex("^Done$"), self.done_handler),
]
))

View File

View File

@ -0,0 +1,88 @@
from typing import BinaryIO, List, Optional
import requests
import dataclasses
@dataclasses.dataclass
class Tag:
name: str
value: Optional[str]
@dataclasses.dataclass
class Page:
id: Optional[int] = dataclasses.field(default=None)
book_id: Optional[int] = dataclasses.field(default=None)
chapter_id: Optional[int] = dataclasses.field(default=None)
name: Optional[str] = dataclasses.field(default=None)
html: Optional[str] = dataclasses.field(default=None)
markdown: Optional[str] = dataclasses.field(default=None)
tags: List[Tag] = dataclasses.field(default=None)
class BookstackClient:
def __init__(self, url, token_id, token_secret):
self.wiki_url = url
self.token_id = token_id
self.token_secret = token_secret
self.session = requests.Session()
self.session.headers = {'Authorization': f"Token {self.token_id}:{self.token_secret}"}
def _get(self, datatype, filters={}):
query = {}
for key,value in filters.items():
query[f'filter[{key}]']=value
r = self.session.get(self.wiki_url + f"/api/{datatype}", params=query)
return r.json()
def add_attachment_from_url(self, page_id: int, name: str, link: str):
r = self.session.post(self.wiki_url + f"/api/attachments",
data={'name':name, 'uploaded_to':page_id, 'link':link}
)
return r.json()
def add_attachment_from_upload(self, page_id: int, name: str, file: BinaryIO):
r = self.session.post(self.wiki_url + f"/api/attachments",
data={'name':name, 'uploaded_to':page_id},
files={'file': file}
)
return r.json()
def get_pages(self, filters={}):
return self._get('pages', filters)
def get_page(self, id: int):
r = self.session.get(self.wiki_url + f"/api/pages/{id}")
return r.json()
def create_page(self, page: Page):
r = self.session.post(self.wiki_url + "/api/pages", data=dataclasses.asdict(page) )
return r.json()
def update_page(self, page: Page):
r = self.session.put(self.wiki_url + f"/api/pages/{page.id}", data=dataclasses.asdict(page) )
return r.json()
def get_chapters(self, filters={}):
return self._get('chapters', filters)
def get_books(self, filters={}):
return self._get('books', filters)
def get_shelves(self, filters={}):
return self._get('shelves', filters)

View File

@ -0,0 +1,2 @@
import feedparser