diff --git a/.bot_storage/db.sqlite b/.bot_storage/db.sqlite deleted file mode 100644 index 466c21e..0000000 Binary files a/.bot_storage/db.sqlite and /dev/null differ diff --git a/.gitignore b/.gitignore index 4656419..7655e78 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ dev.env secret.yaml +# Static data +.bot_storage/ + # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..bbcb7be --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Python: Current File", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": true + }, + { + "name": "Python: Current project", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/bot/main.py", + "console": "integratedTerminal", + "justMyCode": true, + "envFile": "${workspaceFolder}/dev.env", + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index c0c6476..64a36dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM python:3.10 -ENV dockerized=true +ENV DOCKERIZED=true WORKDIR /app diff --git a/bot/commands/journal.py b/bot/commands/journal.py index 0ca07bf..f813f94 100644 --- a/bot/commands/journal.py +++ b/bot/commands/journal.py @@ -1,6 +1,6 @@ import datetime - -from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler, CallbackContext +import os +from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler from telegram import InlineKeyboardButton, InlineKeyboardMarkup DATE_CHOICE, DATE_ENTRY, CONTENT = range(3) @@ -28,8 +28,15 @@ class JournalHandler: self.current_model = None + async def start(self, update, context): """Send a message when the command /start is issued.""" + if os.getenv("DOCKERIZED", "false") == "true" and os.getenv("CHAT_ID") != str(update.message.chat_id): + await update.message.reply_text("You are not authorized to use this bot") + return ConversationHandler.END + + print(f"User: {update.message.from_user.id}") + print(f"Chat: {update.message.chat_id}") options = [ InlineKeyboardButton("Today", callback_data="today"), @@ -40,28 +47,36 @@ class JournalHandler: await update.message.reply_text("Please choose an option for the entry:", reply_markup=keyboard) return DATE_CHOICE + async def date_choice(self, update, context): query = update.callback_query - query.answer() + await query.answer() if query.data == "today" or query.data == "yesterday": date = datetime.datetime.now().date() if query.data == "today" else datetime.datetime.now().date() - datetime.timedelta(days=1) - self.current_model = self.models.JournalEntry( + self.current_model, new = self.models.JournalEntry.get_or_create( date = date ) + if not new: + await query.edit_message_text(text="An entry already exists for this date") + return ConversationHandler.END + await query.edit_message_text( + text="Please enter the content for the entry" + ) return CONTENT else: await query.edit_message_text(text="Please enter the date in the format DDMMYYYY") return DATE_ENTRY async def date_entry(self, update, context): - # create an inline keyboard with the option today and yesterday and custom - # date date = update.message.text try: date = datetime.datetime.strptime(date, "%d%m%Y").date() - self.current_model = self.models.JournalEntry( + self.current_model, new = self.models.JournalEntry.get_or_create( date = date ) + if not new: + await update.message.reply_text("An entry already exists for this date") + return ConversationHandler.END except ValueError: await update.message.reply_text("Please enter the date in the format DDMMYYYY") return DATE_ENTRY @@ -69,8 +84,26 @@ class JournalHandler: await update.message.reply_text("Please enter the content for the entry") return CONTENT + async def content_text(self, update, context): + self.current_model.text = update.message.text + self.current_model.author_id = update.message.from_user.id + self.current_model.save() + return ConversationHandler.END + - return async def content_media(self, update, context): - return \ No newline at end of file + self.current_model.author_id = update.message.from_user.id + + if update.message.photo: + file = await update.message.effective_attachment[-1].get_file() + else: + file = await update.message.effective_attachment.get_file() + + file_bytes = await file.download_as_bytearray() + file_path = file.file_path + self.current_model.save_media(file_bytes, file_path) + + self.current_model.text = update.message.caption + self.current_model.save() + return ConversationHandler.END diff --git a/bot/models.py b/bot/models.py index 122b27c..e0f07bb 100644 --- a/bot/models.py +++ b/bot/models.py @@ -1,5 +1,15 @@ from peewee import * from pathlib import Path +import os + +ID_MAPPINGS = { + "Lia": 0, + "Rémy": 364520272, +} +ID_MAPPINGS_REV = dict((v, k) for k, v in ID_MAPPINGS.items()) + +MEDIA_DIR = Path(os.getenv("MEDIA_DIR")) +MEDIA_DIR.mkdir(parents=True, exist_ok=True) db = DatabaseProxy() @@ -11,25 +21,38 @@ class BaseModel(Model): # model for a single journal entry class JournalEntry(BaseModel): - # the date of the entry - date = DateField() - # the text of the entry - text = TextField() - media_path = TextField() - author = TextField() + date = DateField(unique=True) + author = TextField(null=True) + text = TextField(null=True) + media_path = TextField(null=True) @property def media(self): return Path(self.media_path).open('rb') - @media.setter - def media(self, media): - self.media_path = Path(media).absolute() + def save_media(self, media: bytearray, file_name: str): + ext = Path(file_name).suffix + file_name = f"{self.date.isoformat()}-media{ext}" + self.media_path = MEDIA_DIR / file_name + self.media_path.write_bytes(media) self.save() + @property + def author_id(self): + if self.author is None: + return None + else: + return ID_MAPPINGS[self.author] + + @author_id.setter + def author_id(self, author_id): + self.author = ID_MAPPINGS_REV[author_id] + self.save() + + def set_db(db_path): db.initialize(SqliteDatabase(db_path)) db.connect() - db.create_tables([JournalEntry], safe=True) \ No newline at end of file + db.create_tables([JournalEntry], safe=True) diff --git a/deployment/deployment.yaml b/deployment/deployment.yaml index e69de29..5779419 100644 --- a/deployment/deployment.yaml +++ b/deployment/deployment.yaml @@ -0,0 +1,70 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: journal + name: journal-bot + labels: + app: journal-bot +spec: +# deployment running a single container + selector: + matchLabels: + app: journal-bot + replicas: 1 + template: + metadata: + labels: + app: journal-bot + spec: + containers: + - name: journal + image: mollre/journal:arm64 + envFrom: + - secretRef: + name: journal-secret-env + env: + - name: MEDIA_DIR + value: /journal/media + volumeMounts: + - name: journal-nfs + mountPath: /journal + volumes: + - name: journal-nfs + persistentVolumeClaim: + claimName: journal-data-nfs +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + namespace: journal + name: "journal-data-nfs" + labels: + directory: "journal-data" +spec: + storageClassName: fast + capacity: + storage: "100Mi" + accessModes: + - ReadWriteOnce + nfs: + path: /journal-data + server: 10.43.239.43 # assigned to nfs-server service. Won't change as long as service is not redeployed + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: journal + name: "journal-data-nfs" +spec: + storageClassName: "fast" + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "100Mi" + selector: + matchLabels: + directory: "journal-data" + +---