Compare commits
	
		
			96 Commits
		
	
	
		
			secretbran
			...
			ac5311c7d0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ac5311c7d0 | |||
| d8407bac65 | |||
| 2f9d94406c | |||
| 1ca13a9451 | |||
| 0cdc359463 | |||
| 2d923df965 | |||
| 421c3a7e1f | |||
|   | 9eafa55dd8 | ||
| 2a344817f7 | |||
| 27656c21ae | |||
|   | b733d1040c | ||
| 6d9c60b0d7 | |||
| 84fb43e836 | |||
|   | cfcea80a64 | ||
| 7daf30f851 | |||
|   | a030d06641 | ||
| 15304d565e | |||
|   | 6094112f48 | ||
|   | c75fb0b7a3 | ||
|   | b2820ce902 | ||
|   | b605ce315b | ||
|   | dc14eb0aec | ||
|   | 393ac72191 | ||
|   | f235b27916 | ||
|   | c4fbd089cc | ||
|   | ec87c6751c | ||
| 724b17c4b7 | |||
|   | 8b7a318e6b | ||
|   | a0e1aaa779 | ||
| c9254a3e88 | |||
|   | 3097594482 | ||
| 49df5a4495 | |||
|   | 9eb7f5bb77 | ||
| 35dbbe4ece | |||
| 6b63276dd7 | |||
|   | d40afca1a4 | ||
| 0e2b714848 | |||
| df55dbf6c7 | |||
|   | c1e7c0eb38 | ||
| c58e256194 | |||
|   | cbaedb04cb | ||
| 008cf08163 | |||
| b1a9e5fa46 | |||
|   | 7e3cf46765 | ||
| d6447c54d1 | |||
|   | db44a38eb7 | ||
|   | a77ab50f82 | ||
|   | 63daf6845c | ||
|   | 7d793c571c | ||
|   | a805775707 | ||
|   | 9ee7302f65 | ||
|   | 58fb79820f | ||
|   | 9518b813f6 | ||
|   | 122b090475 | ||
|   | 6a100e7364 | ||
|   | 0a14e8be4a | ||
|   | bdb58c8a5f | ||
|   | c526c23e3a | ||
|   | 8ff3f05034 | ||
|   | 1ce47df5f4 | ||
|   | fa5427ec09 | ||
|   | 378795d3ef | ||
|   | 8ea0c2f517 | ||
|   | 3622e76639 | ||
|   | d5349433d4 | ||
|   | 282a3d305d | ||
|   | e5d24b0171 | ||
|   | 0fb841fce4 | ||
|   | 3507d16394 | ||
|   | 6952e7f12f | ||
|   | 674a88f6ab | ||
|   | 11aefc0322 | ||
|   | 7273dd5121 | ||
|   | 350bd19b08 | ||
| e3f643e805 | |||
| 970d6931db | |||
| b39aa6ecb1 | |||
| 9b54f05d75 | |||
|   | cd7594350f | ||
| c202ad8035 | |||
|   | 50e7226709 | ||
|   | ae330eb389 | ||
| ebc89b48e2 | |||
| 0f49cbf4fb | |||
| cd3bbe9b00 | |||
| 1bc829f53d | |||
| 3975ca5997 | |||
| 2afde219a9 | |||
| a666b8e9ae | |||
| e77c106813 | |||
| 86a9762f39 | |||
| 3455946996 | |||
| 3f58eab8c7 | |||
|   | 1613e05b61 | ||
|   | 0860196a53 | ||
| 49339ebcb9 | 
| @@ -16,7 +16,10 @@ steps: | |||||||
|       from_secret: docker_pw |       from_secret: docker_pw | ||||||
|  |  | ||||||
|     repo: mollre/journal-bot |     repo: mollre/journal-bot | ||||||
|     tags: latest |     tags:  | ||||||
|  |       - 1.0.${DRONE_BUILD_NUMBER} | ||||||
|  |       - latest | ||||||
|  |     build_args: "BOT_VERSION=1.0.${DRONE_BUILD_NUMBER}" | ||||||
|  |  | ||||||
|  |  | ||||||
| trigger: | trigger: | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							| @@ -4,15 +4,6 @@ | |||||||
|     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 |     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||||
|     "version": "0.2.0", |     "version": "0.2.0", | ||||||
|     "configurations": [ |     "configurations": [ | ||||||
|  |  | ||||||
|         { |  | ||||||
|             "name": "Python: Current File", |  | ||||||
|             "type": "python", |  | ||||||
|             "request": "launch", |  | ||||||
|             "program": "${file}", |  | ||||||
|             "console": "integratedTerminal", |  | ||||||
|             "justMyCode": true |  | ||||||
|         }, |  | ||||||
|         { |         { | ||||||
|             "name": "Python: Current project", |             "name": "Python: Current project", | ||||||
|             "type": "python", |             "type": "python", | ||||||
|   | |||||||
| @@ -1,11 +1,16 @@ | |||||||
| FROM python:3.10-slim | FROM python:3-slim | ||||||
| ENV DOCKERIZED=true | ENV DOCKERIZED=true | ||||||
|  | ARG BOT_VERSION | ||||||
|  | # set at build time | ||||||
|  | ENV BOT_VERSION=$BOT_VERSION | ||||||
|  |  | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
|  | RUN pip install pipenv | ||||||
|  |  | ||||||
|  |  | ||||||
| COPY Pipfile Pipfile.lock ./ | COPY Pipfile Pipfile.lock ./ | ||||||
|  |  | ||||||
| RUN pip install pipenv && pipenv install --system --deploy | RUN pipenv install --system --deploy | ||||||
|  |  | ||||||
| COPY bot . | COPY bot . | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								Pipfile
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								Pipfile
									
									
									
									
									
								
							| @@ -4,7 +4,11 @@ verify_ssl = true | |||||||
| name = "pypi" | name = "pypi" | ||||||
|  |  | ||||||
| [packages] | [packages] | ||||||
| python-telegram-bot = "*" |  | ||||||
| peewee = "*" | peewee = "*" | ||||||
|  | python-telegram-bot = {extras = ["job-queue"], version = "*"} | ||||||
|  | anyio = "*" | ||||||
|  |  | ||||||
| [dev-packages] | [dev-packages] | ||||||
|  |  | ||||||
|  | [pipenv] | ||||||
|  | allow_prereleases = true | ||||||
|   | |||||||
							
								
								
									
										79
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										79
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|     "_meta": { |     "_meta": { | ||||||
|         "hash": { |         "hash": { | ||||||
|             "sha256": "e95b9deab62bd0c661f20a178b8701fc84420db5f663fa4416666e1d05f6ce76" |             "sha256": "5458e81c4f85af776acc44f46af838644ef8c00ccf4223fbe06f9d76a4717fc6" | ||||||
|         }, |         }, | ||||||
|         "pipfile-spec": 6, |         "pipfile-spec": 6, | ||||||
|         "requires": {}, |         "requires": {}, | ||||||
| @@ -16,19 +16,26 @@ | |||||||
|     "default": { |     "default": { | ||||||
|         "anyio": { |         "anyio": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421", |                 "sha256:48d53f0b141f5757c38d648309e6fe254857fae092d67f938fa248d7c0f36804", | ||||||
|                 "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3" |                 "sha256:596b09c520820e7eed961ddc889540972f92d5e8fcb081117fc054c409df34ae" | ||||||
|             ], |             ], | ||||||
|             "markers": "python_full_version >= '3.6.2'", |             "index": "pypi", | ||||||
|             "version": "==3.6.2" |             "version": "==4.0.0rc1" | ||||||
|  |         }, | ||||||
|  |         "apscheduler": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:0293937d8f6051a0f493359440c1a1b93e882c57daf0197afeff0e727777b96e", | ||||||
|  |                 "sha256:e813ad5ada7aff36fb08cdda746b520531eaac7757832abc204868ba78e0c8f6" | ||||||
|  |             ], | ||||||
|  |             "version": "==3.10.1" | ||||||
|         }, |         }, | ||||||
|         "certifi": { |         "certifi": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", |                 "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", | ||||||
|                 "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" |                 "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" | ||||||
|             ], |             ], | ||||||
|             "markers": "python_version >= '3.6'", |             "markers": "python_version >= '3.6'", | ||||||
|             "version": "==2022.12.7" |             "version": "==2023.7.22" | ||||||
|         }, |         }, | ||||||
|         "h11": { |         "h11": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @@ -40,19 +47,19 @@ | |||||||
|         }, |         }, | ||||||
|         "httpcore": { |         "httpcore": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb", |                 "sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888", | ||||||
|                 "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0" |                 "sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87" | ||||||
|             ], |             ], | ||||||
|             "markers": "python_version >= '3.7'", |             "markers": "python_version >= '3.7'", | ||||||
|             "version": "==0.16.3" |             "version": "==0.17.3" | ||||||
|         }, |         }, | ||||||
|         "httpx": { |         "httpx": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9", |                 "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd", | ||||||
|                 "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6" |                 "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd" | ||||||
|             ], |             ], | ||||||
|             "markers": "python_version >= '3.7'", |             "markers": "python_version >= '3.7'", | ||||||
|             "version": "==0.23.3" |             "version": "==0.24.1" | ||||||
|         }, |         }, | ||||||
|         "idna": { |         "idna": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @@ -70,22 +77,38 @@ | |||||||
|             "version": "==3.16.2" |             "version": "==3.16.2" | ||||||
|         }, |         }, | ||||||
|         "python-telegram-bot": { |         "python-telegram-bot": { | ||||||
|  |             "extras": [ | ||||||
|  |                 "job-queue" | ||||||
|  |             ], | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:4d1d4b643ce158aa17a0987b84005eaf25fe0ce8b38fd234099594985611c198", |                 "sha256:a6ac3f9c9674aaf7d1c7e652d8b75cde969fb872f75e9521b8516eceaba82b1b", | ||||||
|                 "sha256:d0aa53e1f06d7cb7919cc0e2d6c81a02d968fc29921aeaa962edd1efb816a9bd" |                 "sha256:e426404b0006989a5bcc05e11a7ef3ffe0c086b684a4e963db5bda1d361a049a" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==20.2" |             "version": "==20.4" | ||||||
|         }, |         }, | ||||||
|         "rfc3986": { |         "pytz": { | ||||||
|             "extras": [ |  | ||||||
|                 "idna2008" |  | ||||||
|             ], |  | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835", |                 "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588", | ||||||
|                 "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97" |                 "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb" | ||||||
|             ], |             ], | ||||||
|             "version": "==1.5.0" |             "version": "==2023.3" | ||||||
|  |         }, | ||||||
|  |         "setuptools": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f", | ||||||
|  |                 "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235" | ||||||
|  |             ], | ||||||
|  |             "markers": "python_version >= '3.7'", | ||||||
|  |             "version": "==68.0.0" | ||||||
|  |         }, | ||||||
|  |         "six": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", | ||||||
|  |                 "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" | ||||||
|  |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|  |             "version": "==1.16.0" | ||||||
|         }, |         }, | ||||||
|         "sniffio": { |         "sniffio": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @@ -94,6 +117,14 @@ | |||||||
|             ], |             ], | ||||||
|             "markers": "python_version >= '3.7'", |             "markers": "python_version >= '3.7'", | ||||||
|             "version": "==1.3.0" |             "version": "==1.3.0" | ||||||
|  |         }, | ||||||
|  |         "tzlocal": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:46eb99ad4bdb71f3f72b7d24f4267753e240944ecfc16f25d2719ba89827a803", | ||||||
|  |                 "sha256:f3596e180296aaf2dbd97d124fe76ae3a0e3d32b258447de7b939b3fd4be992f" | ||||||
|  |             ], | ||||||
|  |             "markers": "python_version >= '3.7'", | ||||||
|  |             "version": "==5.0.1" | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|     "develop": {} |     "develop": {} | ||||||
|   | |||||||
| @@ -1,3 +1,9 @@ | |||||||
| # journal-bot | # journal-bot | ||||||
|  |  | ||||||
| Sharing memories, the digital way... | Sharing memories, the digital way... | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Migration 10.03.24 | ||||||
|  | ``` | ||||||
|  | ALTER TABLE journalentry ADD COLUMN rating INTEGER; | ||||||
|  | ``` | ||||||
|   | |||||||
| @@ -2,12 +2,21 @@ from datetime import date | |||||||
| from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler, CallbackContext | from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler, CallbackContext | ||||||
| from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, InputMediaPhoto | from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, InputMediaPhoto | ||||||
| import random | import random | ||||||
|  | import os | ||||||
|  | from pathlib import Path | ||||||
| # ACTION_CHOICE, DATE_ENTRY, ADD_CONTENT = range(3) | # ACTION_CHOICE, DATE_ENTRY, ADD_CONTENT = range(3) | ||||||
| ACTION, TUERCHEN_CHOICE = range(2) | ACTION, TUERCHEN_CHOICE = range(2) | ||||||
|  |  | ||||||
|  |  | ||||||
| from .basehandler import BaseHandler | from .basehandler import BaseHandler | ||||||
|  |  | ||||||
|  | MEDIA_DIR = Path(os.getenv("MEDIA_DIR")) | ||||||
|  | GIF_LOCATION = MEDIA_DIR / "advent" / "gifs" | ||||||
|  | # GIFS = list(GIF_LOCATION.glob("*.mp4")) | ||||||
|  | STICKER_LOCATION = MEDIA_DIR / "advent" / "stickers" | ||||||
|  | # STICKERS = list(STICKER_LOCATION.glob("*.tgs")) | ||||||
|  | PICTURE_LOCATION = MEDIA_DIR / "advent" / "pretty_pictures" | ||||||
|  | PICTURES = list(PICTURE_LOCATION.glob("*.jpg")) | ||||||
|  |  | ||||||
| class AdventsHandler(BaseHandler): | class AdventsHandler(BaseHandler): | ||||||
|     def __init__(self, entry_string): |     def __init__(self, entry_string): | ||||||
|         self.entry_string = entry_string |         self.entry_string = entry_string | ||||||
| @@ -66,12 +75,11 @@ class AdventsHandler(BaseHandler): | |||||||
|         keyboard = [[InlineKeyboardButton("Bubo Küsschen", callback_data="kuss")], [InlineKeyboardButton("Türchen öffnen", callback_data="tuer")], [InlineKeyboardButton("Pretty Bubo Picture", callback_data="picture")]] |         keyboard = [[InlineKeyboardButton("Bubo Küsschen", callback_data="kuss")], [InlineKeyboardButton("Türchen öffnen", callback_data="tuer")], [InlineKeyboardButton("Pretty Bubo Picture", callback_data="picture")]] | ||||||
|  |  | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|         await update.message.reply_sticker(sticker=open(".bot_storage\stickers\stickerwhat.tgs", "rb")) |         await update.message.reply_sticker(sticker=STICKER_LOCATION/"stickerwhat.tgs") | ||||||
|         await update.message.reply_text(text="Hallo, mein süßer Weihnachts-Bubo! Ich bin dein Adventskalender ^^. Was möchtest du tun?", reply_markup=reply_markup) |         await update.message.reply_text(text="Hallo, mein süßer Weihnachts-Bubo! Ich bin dein Adventskalender ^^. Was möchtest du tun?", reply_markup=reply_markup) | ||||||
|  |  | ||||||
|         return ACTION |         return ACTION | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def kuesschen(self, update: Update, context: CallbackContext): |     async def kuesschen(self, update: Update, context: CallbackContext): | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         await query.answer() |         await query.answer() | ||||||
| @@ -79,7 +87,7 @@ class AdventsHandler(BaseHandler): | |||||||
|         keyboard = [[InlineKeyboardButton("Kalendertürchen öffnen", callback_data="tuer")], [InlineKeyboardButton("Noch ein Küsschen!", callback_data="kuss")], [InlineKeyboardButton("Pretty Bubo Picture", callback_data="picture")], [InlineKeyboardButton("Bis zum nächsten Mal!", callback_data="bye")]] |         keyboard = [[InlineKeyboardButton("Kalendertürchen öffnen", callback_data="tuer")], [InlineKeyboardButton("Noch ein Küsschen!", callback_data="kuss")], [InlineKeyboardButton("Pretty Bubo Picture", callback_data="picture")], [InlineKeyboardButton("Bis zum nächsten Mal!", callback_data="bye")]] | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|  |  | ||||||
|         await update.effective_message.reply_sticker(sticker=open(".bot_storage\stickers\stickerkiss.tgs", "rb")) |         await update.effective_message.reply_sticker(sticker=STICKER_LOCATION/"stickerkiss.tgs") | ||||||
|         await update.effective_message.reply_text(text="Mua!", reply_markup=reply_markup) |         await update.effective_message.reply_text(text="Mua!", reply_markup=reply_markup) | ||||||
|  |  | ||||||
|         return ACTION |         return ACTION | ||||||
| @@ -101,7 +109,8 @@ class AdventsHandler(BaseHandler): | |||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|  |  | ||||||
|         picture_number = random.randint(1,31) |         picture_number = random.randint(1,31) | ||||||
|         await update.effective_message.reply_photo(photo=open(".bot_storage\pretty_pictures\photo_"+f"{picture_number}"+"_2023-11-25_14-25-53.jpg", "rb"), caption="So ein cutes Foto!", reply_markup=reply_markup) |         #print(picture_number-1) | ||||||
|  |         await update.effective_message.reply_photo(photo=PICTURES[picture_number-1], caption="So ein cutes Foto!", reply_markup=reply_markup) | ||||||
|  |  | ||||||
|         return ACTION |         return ACTION | ||||||
|      |      | ||||||
| @@ -116,11 +125,11 @@ class AdventsHandler(BaseHandler): | |||||||
|  |  | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|          |          | ||||||
|         #if tuer_nummer <= int(date.today().strftime("%d")): |         datum_tuer_heute = date(2023, 12, tuer_nummer) | ||||||
|         if (tuer_nummer <= int(date.today().strftime("%d"))) and (int(date.today().strftime("%m"))==12): |         if (date.today() - datum_tuer_heute).days >= 0: | ||||||
|             await update.message.reply_document(document=open(".bot_storage\gifs\gif"+f"{tuer_nummer}"+".mp4", "rb"),caption=f"Türchen für den {tuer_nummer}. Dezember: \n" + self.tuerchen_texte[tuerchen], reply_markup=reply_markup) |             await update.message.reply_document(document=GIF_LOCATION/ f"gif{tuer_nummer}.mp4", caption=f"Türchen für den {tuer_nummer}. Dezember: \n" + self.tuerchen_texte[tuerchen], reply_markup=reply_markup) | ||||||
|         else: |         else: | ||||||
|             await update.message.reply_sticker(sticker=open(".bot_storage\stickers\stickerangry.tgs", "rb")) |             await update.message.reply_sticker(sticker=STICKER_LOCATION/"stickerangry.tgs") | ||||||
|             await update.message.reply_text(text="Hey, nicht schummeln! Dieses Türchen darfst du noch nicht sehen.", reply_markup=reply_markup) |             await update.message.reply_text(text="Hey, nicht schummeln! Dieses Türchen darfst du noch nicht sehen.", reply_markup=reply_markup) | ||||||
|  |  | ||||||
|         return ACTION |         return ACTION | ||||||
| @@ -129,7 +138,7 @@ class AdventsHandler(BaseHandler): | |||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         await query.answer() |         await query.answer() | ||||||
|  |  | ||||||
|         await update.effective_message.reply_sticker(sticker=open(".bot_storage\stickers\stickerbye.tgs", "rb")) |         await update.effective_message.reply_sticker(sticker=STICKER_LOCATION/"stickerbye.tgs") | ||||||
|         await update.effective_message.reply_text(text="Bye bye, Bubo! Hab dich ganz doll lieb! 😘") |         await update.effective_message.reply_text(text="Bye bye, Bubo! Hab dich ganz doll lieb! 😘") | ||||||
|  |  | ||||||
|         return ConversationHandler.END |         return ConversationHandler.END | ||||||
|   | |||||||
| @@ -5,4 +5,4 @@ class BaseHandler: | |||||||
|     entry_string: str |     entry_string: str | ||||||
|  |  | ||||||
|     async def entry_point(self, update, context) -> None: |     async def entry_point(self, update, context) -> None: | ||||||
|         self.logger.info(f"Chat said: {self.entry_string}") |         self.logger.info(f"Chat ({update.message.chat_id}) said: {self.entry_string}") | ||||||
| @@ -2,28 +2,33 @@ import datetime | |||||||
| import os | import os | ||||||
| from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler | from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler | ||||||
| from telegram import InlineKeyboardButton, InlineKeyboardMarkup | from telegram import InlineKeyboardButton, InlineKeyboardMarkup | ||||||
| ACTION_CHOICE, DATE_ENTRY, ADD_CONTENT = range(3) | from telegram.constants import ParseMode | ||||||
|  | import models | ||||||
|  |  | ||||||
|  | ENTRY_OPTIONS, CONTENT_ENTRY, DAY_RATING = range(3) | ||||||
|  | BUTTON_COUNT = 5 | ||||||
|  |  | ||||||
|  |  | ||||||
| from .basehandler import BaseHandler | from .basehandler import BaseHandler | ||||||
|  |  | ||||||
| class JournalHandler(BaseHandler): | class JournalHandler(BaseHandler): | ||||||
|     def __init__(self, entry_string, models): |     def __init__(self, entry_string): | ||||||
|         self.models = models |  | ||||||
|         self.entry_string = entry_string |         self.entry_string = entry_string | ||||||
|         self.handler = ConversationHandler( |         self.handler = ConversationHandler( | ||||||
|             entry_points=[CommandHandler(entry_string, self.entry_point)], |             entry_points=[CommandHandler(entry_string, self.entry_point)], | ||||||
|             states={ |             states={ | ||||||
|                 ACTION_CHOICE: [ |                 ENTRY_OPTIONS: [ | ||||||
|                     CallbackQueryHandler(self.date_choice, pattern="today|yesterday"), |                     CallbackQueryHandler(self.date_button, pattern=r"^\d{8}$"), # a serialized date | ||||||
|                     CallbackQueryHandler(self.date_custom, pattern="custom"), |                     CallbackQueryHandler(self.date_custom, pattern=r"^\d{1,3}$"), # a ~ small delta, symbolizing a new range to show | ||||||
|                     CallbackQueryHandler(self.option_delete, pattern="delete") |                     CallbackQueryHandler(self.option_delete, pattern="delete"), | ||||||
|                     ], |  | ||||||
|                 DATE_ENTRY: [ |  | ||||||
|                     MessageHandler(filters.ALL, self.date_entry), |                     MessageHandler(filters.ALL, self.date_entry), | ||||||
|                     ], |                     ], | ||||||
|                 ADD_CONTENT: [ |                 CONTENT_ENTRY: [ | ||||||
|                     MessageHandler(filters.ALL, self.content_save), |                     MessageHandler(filters.ALL, self.content_save), | ||||||
|                     ] |                     ], | ||||||
|  |                 DAY_RATING: [ | ||||||
|  |                     CallbackQueryHandler(self.day_rating_save), | ||||||
|  |                     ], | ||||||
|             }, |             }, | ||||||
|             fallbacks=[], |             fallbacks=[], | ||||||
|         ) |         ) | ||||||
| @@ -37,52 +42,74 @@ class JournalHandler(BaseHandler): | |||||||
|             await update.message.reply_text("You are not authorized to use this bot") |             await update.message.reply_text("You are not authorized to use this bot") | ||||||
|             return ConversationHandler.END |             return ConversationHandler.END | ||||||
|  |  | ||||||
|  |         dates = [(datetime.datetime.now() - datetime.timedelta(days = i)).date() for i in range(BUTTON_COUNT + 2)][::-1] | ||||||
|  |         # since there are two buttons additional buttons, we need to have two more days | ||||||
|  |         names = get_names(dates) | ||||||
|  |         callbacks = [d.strftime("%d%m%Y") for d in dates] | ||||||
|  |  | ||||||
|         options = [[ |         options = [ | ||||||
|                 InlineKeyboardButton("Today", callback_data="today"), |                 [InlineKeyboardButton(n, callback_data=c)] for n,c in zip(names[::-1], callbacks[::-1]) | ||||||
|                 InlineKeyboardButton("Yesterday", callback_data="yesterday"), |         ] + [ | ||||||
|                 InlineKeyboardButton("Custom date", callback_data="custom"), |             [ | ||||||
|  |                 InlineKeyboardButton("<<", callback_data=BUTTON_COUNT + 2) | ||||||
|             ], |             ], | ||||||
|             [ |             [ | ||||||
|                 InlineKeyboardButton("Delete", callback_data="delete") |                 InlineKeyboardButton("Delete", callback_data="delete") | ||||||
|             ] |             ] | ||||||
|         ] |         ] | ||||||
|         keyboard = InlineKeyboardMarkup(options) |         keyboard = InlineKeyboardMarkup(options) | ||||||
|         await update.message.reply_text("Please choose an option for the entry:", reply_markup=keyboard) |         await update.message.reply_text("Please choose a date \(or type it in the format _DDMMYYYY_\)", reply_markup=keyboard, parse_mode=ParseMode.MARKDOWN_V2) | ||||||
|         return ACTION_CHOICE |         return ENTRY_OPTIONS | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def date_choice(self, update, context): |     async def date_button(self, update, context): | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         await query.answer() |         await query.answer() | ||||||
|         if query.data == "today": |         date = datetime.datetime.strptime(query.data, "%d%m%Y").date() | ||||||
|             date = datetime.datetime.now().date() |  | ||||||
|         elif query.data == "yesterday": |  | ||||||
|             date = datetime.datetime.now().date() - datetime.timedelta(days=1) |  | ||||||
|         else: |  | ||||||
|             raise ValueError("Invalid date choice") |  | ||||||
|  |  | ||||||
|         with self.models.db: |         with models.db: | ||||||
|             self.current_model, new = self.models.JournalEntry.get_or_create( |             self.current_model, new = models.JournalEntry.get_or_create( | ||||||
|                 date = date |                 date = date | ||||||
|             ) |             ) | ||||||
|         if new: |         if new: | ||||||
|  |             count = models.JournalEntry.select().count() | ||||||
|             await query.edit_message_text( |             await query.edit_message_text( | ||||||
|                 text=f"What is your entry for {self.current_model.date_pretty}?" |                 text=f"Journal entry no. {count}. What happened on {self.current_model.date_pretty}?" | ||||||
|             ) |             ) | ||||||
|         else: |         else: | ||||||
|             await query.edit_message_text(text="An entry already exists for this date") |             await query.edit_message_text(text="An entry already exists for this date") | ||||||
|             return ConversationHandler.END |             return ConversationHandler.END | ||||||
|          |          | ||||||
|         return ADD_CONTENT |         return CONTENT_ENTRY | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def date_custom(self, update, context): |     async def date_custom(self, update, context): | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         await query.answer() |         await query.answer() | ||||||
|         await query.edit_message_text(text="Please enter the date in the format DDMMYYYY") |         delta = int(query.data) | ||||||
|         return DATE_ENTRY |  | ||||||
|  |  | ||||||
|  |         dates = [(datetime.datetime.now() - datetime.timedelta(days = i + delta)).date() for i in range(BUTTON_COUNT)][::-1] | ||||||
|  |         names = get_names(dates) | ||||||
|  |         callbacks = [d.strftime("%d%m%Y") for d in dates] | ||||||
|  |  | ||||||
|  |         options = [ | ||||||
|  |             [ | ||||||
|  |                 InlineKeyboardButton(">>", callback_data=delta - BUTTON_COUNT) | ||||||
|  |             ] | ||||||
|  |         ] + [ | ||||||
|  |             [InlineKeyboardButton(n, callback_data=c)] for n,c in zip(names[::-1], callbacks[::-1]) | ||||||
|  |         ] + [ | ||||||
|  |             [ | ||||||
|  |                 InlineKeyboardButton("<<", callback_data=delta + BUTTON_COUNT) | ||||||
|  |             ], | ||||||
|  |             [ | ||||||
|  |                 InlineKeyboardButton("Delete", callback_data="delete") | ||||||
|  |             ] | ||||||
|  |         ] | ||||||
|  |         keyboard = InlineKeyboardMarkup(options) | ||||||
|  |         await query.edit_message_text("Please choose a date \(or type it in the format _DDMMYYYY_\)", parse_mode=ParseMode.MARKDOWN_V2, reply_markup=keyboard) | ||||||
|  |  | ||||||
|  |         return ENTRY_OPTIONS | ||||||
|  |  | ||||||
|     async def date_entry(self, update, context): |     async def date_entry(self, update, context): | ||||||
|         date = update.message.text |         date = update.message.text | ||||||
| @@ -90,12 +117,12 @@ class JournalHandler(BaseHandler): | |||||||
|         try: |         try: | ||||||
|             date = datetime.datetime.strptime(date, "%d%m%Y").date() |             date = datetime.datetime.strptime(date, "%d%m%Y").date() | ||||||
|         except ValueError: |         except ValueError: | ||||||
|             await update.message.reply_text("Please enter the date in the format DDMMYYYY") |             await update.message.reply_text("Please enter the date in the format _DDMMYYYY_", parse_mode=ParseMode.MARKDOWN_V2) | ||||||
|             return DATE_ENTRY |             return ENTRY_OPTIONS | ||||||
|          |          | ||||||
|         if context.chat_data.get("delete", False): # if not set, delete was not chosen |         if context.chat_data.get("delete", False): # if not set, delete was not chosen | ||||||
|             with self.models.db: |             with models.db: | ||||||
|                 self.current_model = self.models.JournalEntry.get_or_none( |                 self.current_model = models.JournalEntry.get_or_none( | ||||||
|                     date = date |                     date = date | ||||||
|                 ) |                 ) | ||||||
|             if self.current_model: |             if self.current_model: | ||||||
| @@ -105,20 +132,23 @@ class JournalHandler(BaseHandler): | |||||||
|                 context.chat_data["delete"] = False |                 context.chat_data["delete"] = False | ||||||
|             return ConversationHandler.END |             return ConversationHandler.END | ||||||
|         else: |         else: | ||||||
|             with self.models.db: |             with models.db: | ||||||
|                 self.current_model, new = self.models.JournalEntry.get_or_create( |                 self.current_model, new = models.JournalEntry.get_or_create( | ||||||
|                     date = date |                     date = date | ||||||
|                 ) |                 ) | ||||||
|             if not new: |             if not new: | ||||||
|                 await update.message.reply_text("An entry already exists for this date") |                 await update.message.reply_text("An entry already exists for this date") | ||||||
|                 return ConversationHandler.END |                 return ConversationHandler.END | ||||||
|             else: |             else: | ||||||
|                 await update.message.reply_text(f"What is your entry for {self.current_model.date_pretty}?") |                 count = models.JournalEntry.select().count() | ||||||
|                 return ADD_CONTENT |                 await update.message.reply_text( | ||||||
|  |                     text=f"Journal entry no. {count}. What happened on {self.current_model.date_pretty}?" | ||||||
|  |                 ) | ||||||
|  |                 return CONTENT_ENTRY | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def content_save(self, update, context): |     async def content_save(self, update, context): | ||||||
|         with self.models.db: |         with models.db: | ||||||
|             self.current_model.author_id = update.message.from_user.id |             self.current_model.author_id = update.message.from_user.id | ||||||
|  |  | ||||||
|             if update.message.text: |             if update.message.text: | ||||||
| @@ -137,20 +167,55 @@ class JournalHandler(BaseHandler): | |||||||
|  |  | ||||||
|             self.current_model.save() |             self.current_model.save() | ||||||
|  |  | ||||||
|         await update.message.reply_text(f"Saved entry ✅") |         options = [ | ||||||
|  |             [InlineKeyboardButton(models.RATING_MAPPING[idx], callback_data=idx) for idx in [1,2,3,4,5]] | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |         await update.message.reply_text(f"Saved entry ✅. How was the day?", reply_markup=InlineKeyboardMarkup(options)) | ||||||
|  |  | ||||||
|  |         return DAY_RATING | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     async def day_rating_save(self, update, context): | ||||||
|  |         query = update.callback_query | ||||||
|  |         await query.answer() | ||||||
|  |         rating = int(query.data) | ||||||
|  |         with models.db: | ||||||
|  |             self.current_model.rating = rating | ||||||
|  |             self.current_model.save() | ||||||
|  |         await query.edit_message_text(text="Rating saved ✅") | ||||||
|         return ConversationHandler.END |         return ConversationHandler.END | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def option_delete(self, update, context): |     async def option_delete(self, update, context): | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         await query.answer() |         await query.answer() | ||||||
|         await query.edit_message_text(text="Please enter the date in the format DDMMYYYY") |         await query.edit_message_text(text="Please enter the date in the format _DDMMYYYY_", parse_mode=ParseMode.MARKDOWN_V2) | ||||||
|         context.chat_data["delete"] = True |         context.chat_data["delete"] = True | ||||||
|         return DATE_ENTRY |         return ENTRY_OPTIONS | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def delete_entry(self, update, context): |     async def delete_entry(self, update, context): | ||||||
|         with self.models.db: |         with models.db: | ||||||
|             self.current_model.delete_instance() |             self.current_model.delete_instance() | ||||||
|         context.chat_data["delete"] = False |         context.chat_data["delete"] = False | ||||||
|         await update.message.reply_text(text="Entry deleted ✅") |         await update.message.reply_text(text="Entry deleted ✅") | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### HELPERS | ||||||
|  |  | ||||||
|  | def get_names(dates: list): | ||||||
|  |     names = [] | ||||||
|  |     for d in dates: | ||||||
|  |         suffix = "" | ||||||
|  |         if models.JournalEntry.get_or_none(date = d): | ||||||
|  |             suffix = " ✅" | ||||||
|  |          | ||||||
|  |         if d == datetime.datetime.now().date(): | ||||||
|  |             names.append("Today" + suffix) | ||||||
|  |         elif d == datetime.datetime.now().date() - datetime.timedelta(days = 1): | ||||||
|  |             names.append("Yesterday" + suffix) | ||||||
|  |         else: | ||||||
|  |             names.append(d.strftime("%d.%m.") + suffix) | ||||||
|  |     return names | ||||||
|   | |||||||
| @@ -5,10 +5,7 @@ from telegram import InlineKeyboardButton, InlineKeyboardMarkup | |||||||
|  |  | ||||||
| from .models import ListModel, set_db, db | from .models import ListModel, set_db, db | ||||||
|  |  | ||||||
| MEDIA_DIR = Path(os.getenv("MEDIA_DIR")) | PERSISTENCE_DIR = Path(os.getenv("PERSISTENCE_DIR")) | ||||||
| DB_DIR = MEDIA_DIR / "lists_db" |  | ||||||
| DB_DIR.mkdir(parents=True, exist_ok=True) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| NAME, NEW, ACTION, ITEMADD, ITEMREMOVE, ITEMTOGGLE = range(6) | NAME, NEW, ACTION, ITEMADD, ITEMREMOVE, ITEMTOGGLE = range(6) | ||||||
|  |  | ||||||
| @@ -18,16 +15,17 @@ from ..basehandler import BaseHandler | |||||||
| class ListHandler(BaseHandler): | class ListHandler(BaseHandler): | ||||||
|     """Create and edit lists""" |     """Create and edit lists""" | ||||||
|  |  | ||||||
|     def __init__(self, entry_string, models): |     def __init__(self, entry_string): | ||||||
|         self.journal_models = models # not needed here |  | ||||||
|         self.entry_string = entry_string |         self.entry_string = entry_string | ||||||
|  |  | ||||||
|  |         set_db(PERSISTENCE_DIR / "lists.sqlite") | ||||||
|  |  | ||||||
|         self.list_overview_keyboard = [ |         self.list_overview_keyboard = [ | ||||||
|  |             [InlineKeyboardButton("Print list", callback_data="print")], | ||||||
|             [InlineKeyboardButton("Add item", callback_data="add")], |             [InlineKeyboardButton("Add item", callback_data="add")], | ||||||
|             [InlineKeyboardButton("Toggle item", callback_data="toggle")], |             [InlineKeyboardButton("Toggle item", callback_data="toggle")], | ||||||
|             [InlineKeyboardButton("Remove item", callback_data="remove")], |             [InlineKeyboardButton("Remove item", callback_data="remove")], | ||||||
|             [InlineKeyboardButton("Clear list", callback_data="clear")], |             [InlineKeyboardButton("Clear list", callback_data="clear")], | ||||||
|             [InlineKeyboardButton("Print list", callback_data="print")], |  | ||||||
|             [InlineKeyboardButton("Delete list", callback_data="delete")], |             [InlineKeyboardButton("Delete list", callback_data="delete")], | ||||||
|              |              | ||||||
|         ] |         ] | ||||||
| @@ -40,13 +38,13 @@ class ListHandler(BaseHandler): | |||||||
|                     ], |                     ], | ||||||
|                 NEW : [MessageHandler(filters.TEXT, callback=self.new_listname)], |                 NEW : [MessageHandler(filters.TEXT, callback=self.new_listname)], | ||||||
|                 ACTION: [ |                 ACTION: [ | ||||||
|  |                     CallbackQueryHandler(self.list_print, pattern="^print$"), | ||||||
|                     CallbackQueryHandler(self.list_add, pattern="^add$"), |                     CallbackQueryHandler(self.list_add, pattern="^add$"), | ||||||
|                     CallbackQueryHandler(self.list_toggle, pattern="^toggle$"), |                     CallbackQueryHandler(self.list_toggle, pattern="^toggle$"), | ||||||
|  |                     CallbackQueryHandler(self.list_menu, pattern="^overview$"), | ||||||
|                     CallbackQueryHandler(self.list_remove, pattern="^remove$"), |                     CallbackQueryHandler(self.list_remove, pattern="^remove$"), | ||||||
|                     CallbackQueryHandler(self.list_clear, pattern="^clear$"), |                     CallbackQueryHandler(self.list_clear, pattern="^clear$"), | ||||||
|                     CallbackQueryHandler(self.list_delete, pattern="^delete$"), |                     CallbackQueryHandler(self.list_delete, pattern="^delete$"), | ||||||
|                     CallbackQueryHandler(self.list_print, pattern="^print$"), |  | ||||||
|                     CallbackQueryHandler(self.list_menu, pattern="^overview$"), |  | ||||||
|                     ], |                     ], | ||||||
|                 ITEMADD : [MessageHandler(filters.TEXT, callback=self.list_add_item)], |                 ITEMADD : [MessageHandler(filters.TEXT, callback=self.list_add_item)], | ||||||
|                 ITEMTOGGLE: [CallbackQueryHandler(self.list_toggle_index)], |                 ITEMTOGGLE: [CallbackQueryHandler(self.list_toggle_index)], | ||||||
| @@ -58,10 +56,9 @@ class ListHandler(BaseHandler): | |||||||
|  |  | ||||||
|     async def entry_point(self, update, context) -> None: |     async def entry_point(self, update, context) -> None: | ||||||
|         await super().entry_point(update, context) |         await super().entry_point(update, context) | ||||||
|         set_db(DB_DIR / f"chat_{update.message.chat_id}.db") |  | ||||||
|         with db: |         with db: | ||||||
|             lists = ListModel.select() |             lists = ListModel.select().where(ListModel.chat_id == update.effective_chat.id) | ||||||
|         keyboard = [[InlineKeyboardButton(k.name, callback_data=f"list-{k.name}")] for k in lists] + \ |         keyboard = [[InlineKeyboardButton(k.name, callback_data=f"list-{k.id}")] for k in lists] + \ | ||||||
|             [[InlineKeyboardButton("New list", callback_data="new")]] |             [[InlineKeyboardButton("New list", callback_data="new")]] | ||||||
|  |  | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
| @@ -72,13 +69,13 @@ class ListHandler(BaseHandler): | |||||||
|     async def choose_list(self, update, context: CallbackContext) -> None: |     async def choose_list(self, update, context: CallbackContext) -> None: | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         data = query.data |         data = query.data | ||||||
|         name = data.replace("list-","") |         id = data.replace("list-","") | ||||||
|         await query.answer() |         await query.answer() | ||||||
|         context.user_data["current_list"] = ListModel.get(name = name) |         context.user_data["current_list"] = ListModel.get(id = id) | ||||||
|  |  | ||||||
|         reply_markup = InlineKeyboardMarkup(self.list_overview_keyboard) |         reply_markup = InlineKeyboardMarkup(self.list_overview_keyboard) | ||||||
|  |  | ||||||
|         await query.edit_message_text("Very well. For " + name + " the following actions are available:", reply_markup=reply_markup) |         await query.edit_message_text(f"Using {context.user_data['current_list'].name}. Available actions:", reply_markup=reply_markup) | ||||||
|         return ACTION |         return ACTION | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -88,7 +85,7 @@ class ListHandler(BaseHandler): | |||||||
|  |  | ||||||
|         reply_markup = InlineKeyboardMarkup(self.list_overview_keyboard) |         reply_markup = InlineKeyboardMarkup(self.list_overview_keyboard) | ||||||
|  |  | ||||||
|         await query.edit_message_text(f"Very well. For {context.user_data['current_list'].name} the following actions are available:", reply_markup=reply_markup) |         await query.edit_message_text(f"Using {context.user_data['current_list'].name}. Available actions:", reply_markup=reply_markup) | ||||||
|         return ACTION |         return ACTION | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -103,7 +100,7 @@ class ListHandler(BaseHandler): | |||||||
|         name = update.message.text |         name = update.message.text | ||||||
|         try: |         try: | ||||||
|             with db: |             with db: | ||||||
|                 context.user_data["current_list"] = ListModel.create(name = name) |                 context.user_data["current_list"] = ListModel.create(name = name, chat_id=update.effective_chat.id) | ||||||
|             keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]] |             keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]] | ||||||
|             reply_markup = InlineKeyboardMarkup(keyboard) |             reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|             await update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup) |             await update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup) | ||||||
| @@ -125,10 +122,17 @@ class ListHandler(BaseHandler): | |||||||
|         await query.answer() |         await query.answer() | ||||||
|  |  | ||||||
|         list_object = context.user_data["current_list"] |         list_object = context.user_data["current_list"] | ||||||
|         keyboard = [[InlineKeyboardButton(v, callback_data=k)] for k,v in list_object.content.items()] |         readable_it = printable_list(list_object) | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |  | ||||||
|         |         | ||||||
|         await query.edit_message_text("Which item would you like to toggle?", reply_markup = reply_markup) |         if readable_it: | ||||||
|  |             msg_content = "Which item would you like to toggle?" | ||||||
|  |             keyboard = [[InlineKeyboardButton(v, callback_data=k)] for k,v in zip(list_object.content.keys(), readable_it)] | ||||||
|  |             reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|  |         else: | ||||||
|  |             msg_content = "List empty" | ||||||
|  |             reply_markup = None | ||||||
|  |  | ||||||
|  |         await query.edit_message_text(msg_content, reply_markup = reply_markup) | ||||||
|         return ITEMTOGGLE |         return ITEMTOGGLE | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -170,14 +174,10 @@ class ListHandler(BaseHandler): | |||||||
|         await query.answer() |         await query.answer() | ||||||
|         list_object = context.user_data["current_list"] |         list_object = context.user_data["current_list"] | ||||||
|  |  | ||||||
|         content_it = list_object.content.values() |         readable_it = printable_list(list_object) | ||||||
|         done_it = [ |         | ||||||
|             "· " if e is None \ |         if readable_it: | ||||||
|             else "✅ " if e \ |             msg_content = "\n".join(readable_it) | ||||||
|             else "❌ " \ |  | ||||||
|             for e in list_object.done_dict.values()] |  | ||||||
|         if content_it: |  | ||||||
|             msg_content = "\n".join([f"{d} {c}" for d, c in zip(done_it, content_it)]) |  | ||||||
|         else: |         else: | ||||||
|             msg_content = "List empty" |             msg_content = "List empty" | ||||||
|          |          | ||||||
| @@ -193,7 +193,6 @@ class ListHandler(BaseHandler): | |||||||
|         new = list_object.content |         new = list_object.content | ||||||
|         new.update({"random_key": item}) |         new.update({"random_key": item}) | ||||||
|         list_object.content = new |         list_object.content = new | ||||||
|         # TODO test me! |  | ||||||
|         keyboard = [[InlineKeyboardButton("Add some more", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] |         keyboard = [[InlineKeyboardButton("Add some more", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|         await update.message.reply_text(f"Added {item}", reply_markup=reply_markup) |         await update.message.reply_text(f"Added {item}", reply_markup=reply_markup) | ||||||
| @@ -206,11 +205,10 @@ class ListHandler(BaseHandler): | |||||||
|         await query.answer() |         await query.answer() | ||||||
|  |  | ||||||
|         list_object = context.user_data["current_list"] |         list_object = context.user_data["current_list"] | ||||||
|         old = list_object.done_dict[toggle_key] |         old = list_object.done_dict[toggle_key] or False | ||||||
|         # if all None or all False (first toggle or all false) then set all dones to False |         # if it was previously unset (None), we can later on set it to not old = True | ||||||
|         if not any(list_object.done_dict.values()): |          | ||||||
|             new_done_dict = dict.fromkeys(list_object.done_dict, False) |         new_done_dict = list_object.done_dict | ||||||
|         else: new_done_dict = list_object.done_dict |  | ||||||
|         new_done_dict[toggle_key] = not old |         new_done_dict[toggle_key] = not old | ||||||
|         list_object.done_dict = new_done_dict |         list_object.done_dict = new_done_dict | ||||||
|  |  | ||||||
| @@ -236,3 +234,22 @@ class ListHandler(BaseHandler): | |||||||
|  |  | ||||||
|         await query.edit_message_text(f"Removed {name}", reply_markup=reply_markup) |         await query.edit_message_text(f"Removed {name}", reply_markup=reply_markup) | ||||||
|         return ACTION |         return ACTION | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def printable_list(list_object: ListModel): | ||||||
|  |     content_it = list_object.content.values() | ||||||
|  |     done_bool_it = list_object.done_dict.values() | ||||||
|  |     # distinguish the enumeration: | ||||||
|  |     # either all done_dict values are None -> the list is not toggleable | ||||||
|  |     # or at least one value is of type bool -> the list is toggleable and None === False | ||||||
|  |     if any([type(e) == bool for e in done_bool_it]): | ||||||
|  |         done_it = [ | ||||||
|  |         "✅" if e else "❌" \ | ||||||
|  |         for e in list_object.done_dict.values() | ||||||
|  |         ] | ||||||
|  |     else: | ||||||
|  |         done_it = ["-" for e in done_bool_it] | ||||||
|  |      | ||||||
|  |     readable_it = [f"{d} {c}" for d, c in zip(done_it, content_it)] | ||||||
|  |     return readable_it | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ class BaseModel(Model): | |||||||
|  |  | ||||||
| class ListModel(BaseModel): | class ListModel(BaseModel): | ||||||
|     name = CharField(default="") |     name = CharField(default="") | ||||||
|  |     chat_id = IntegerField() | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def content(self) -> dict: |     def content(self) -> dict: | ||||||
| @@ -45,22 +46,6 @@ class ListEntryModel(BaseModel): | |||||||
|     done = BooleanField(default=None, null=True) |     done = BooleanField(default=None, null=True) | ||||||
|  |  | ||||||
|  |  | ||||||
| # class ListModel(BaseModel): |  | ||||||
| #     name = CharField(unique=True) |  | ||||||
| #     content = TextField(default="") # unlimited length, use to serialise list into |  | ||||||
|  |  | ||||||
| #     @property |  | ||||||
| #     def content_list(self): |  | ||||||
| #         return json.loads(self.content or '[]') |  | ||||||
|      |  | ||||||
| #     @content_list.setter |  | ||||||
| #     def content_list(self, list_content): |  | ||||||
| #         self.content = json.dumps(list_content) |  | ||||||
| #         with db: |  | ||||||
| #             self.save() |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def set_db(db_path): | def set_db(db_path): | ||||||
|     db.initialize(SqliteDatabase(db_path)) |     db.initialize(SqliteDatabase(db_path)) | ||||||
|     with db: |     with db: | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| import datetime | import os | ||||||
| from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler, CallbackContext | from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler, CallbackContext | ||||||
| from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, InputMediaPhoto | from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, InputMediaPhoto | ||||||
|  | import models | ||||||
|  | from telegram.constants import ParseMode | ||||||
| # ACTION_CHOICE, DATE_ENTRY, ADD_CONTENT = range(3) | # ACTION_CHOICE, DATE_ENTRY, ADD_CONTENT = range(3) | ||||||
| MEMORY_CHOICE = range(1) | MEMORY_CHOICE = range(1) | ||||||
|  |  | ||||||
| @@ -8,8 +10,7 @@ MEMORY_CHOICE = range(1) | |||||||
| from .basehandler import BaseHandler | from .basehandler import BaseHandler | ||||||
|  |  | ||||||
| class MemoryHandler(BaseHandler): | class MemoryHandler(BaseHandler): | ||||||
|     def __init__(self, entry_string, models): |     def __init__(self, entry_string): | ||||||
|         self.models = models |  | ||||||
|         self.entry_string = entry_string |         self.entry_string = entry_string | ||||||
|         self.handler = ConversationHandler( |         self.handler = ConversationHandler( | ||||||
|             entry_points=[CommandHandler(entry_string, self.entry_point, )], |             entry_points=[CommandHandler(entry_string, self.entry_point, )], | ||||||
| @@ -27,20 +28,27 @@ class MemoryHandler(BaseHandler): | |||||||
|  |  | ||||||
|     async def entry_point(self, update: Update, context: CallbackContext): |     async def entry_point(self, update: Update, context: CallbackContext): | ||||||
|         await super().entry_point(update, context) |         await super().entry_point(update, context) | ||||||
|  |         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 | ||||||
|  |  | ||||||
|         search_string = " ".join(context.args) |         search_string = " ".join(context.args) | ||||||
|  |  | ||||||
|  |  | ||||||
|         if search_string == '~photo': |         if search_string == '~photo': | ||||||
|             matching_models = self.models.JournalEntry.select().where(self.models.JournalEntry.media_path != "").order_by(self.models.JournalEntry.date) |             matching_models = models.JournalEntry.select().where(models.JournalEntry.media_path != "").order_by(models.JournalEntry.date) | ||||||
|         else: # searching for text |         else: # searching for text | ||||||
|             matching_models = self.models.JournalEntry.select().where( |             matching_models = models.JournalEntry.select().where( | ||||||
|                 self.models.JournalEntry.text.contains( |                 models.JournalEntry.text.contains( | ||||||
|                     search_string |                     search_string | ||||||
|                 ) |                 ) | ||||||
|             ).order_by(self.models.JournalEntry.date) |             ).order_by(models.JournalEntry.date) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         # exit if no memory matches the string | ||||||
|  |         if len(matching_models) == 0: | ||||||
|  |             await update.message.reply_text(f"There is no matching memory yet.") | ||||||
|  |             return ConversationHandler.END | ||||||
|  |  | ||||||
|         options = [[InlineKeyboardButton(m.date_pretty, callback_data=i)] for i,m in enumerate(matching_models)] |         options = [[InlineKeyboardButton(m.date_pretty, callback_data=i)] for i,m in enumerate(matching_models)] | ||||||
|  |  | ||||||
|         keyboard = InlineKeyboardMarkup(options) |         keyboard = InlineKeyboardMarkup(options) | ||||||
| @@ -61,20 +69,23 @@ class MemoryHandler(BaseHandler): | |||||||
|         matching_models = context.chat_data["kept_matches"] |         matching_models = context.chat_data["kept_matches"] | ||||||
|         chosen_match = matching_models[ind] |         chosen_match = matching_models[ind] | ||||||
|  |  | ||||||
|  |         rating_string = f" ({models.RATING_MAPPING[chosen_match.rating]})" if chosen_match.rating else "" | ||||||
|  |  | ||||||
|  |         message_text = f"On {chosen_match.date_pretty}{rating_string}, " \ | ||||||
|  |                 f"{chosen_match.author} wrote: \n" \ | ||||||
|  |                 f"{chosen_match.spoiler_text}" | ||||||
|  |  | ||||||
|         if chosen_match.media_path: |         if chosen_match.media_path: | ||||||
|             # context.bot.sendPhoto() |             # context.bot.sendPhoto() | ||||||
|             await update.effective_message.reply_photo( |             await update.effective_message.reply_photo( | ||||||
|                 photo = chosen_match.media_path, |                 photo = chosen_match.media_path, | ||||||
|                 caption= |                 caption = message_text, | ||||||
|                     f"On {chosen_match.date_pretty}, " |                 parse_mode=ParseMode.HTML | ||||||
|                     f"{chosen_match.author} wrote: \n" |  | ||||||
|                     f"{chosen_match.text}" |  | ||||||
|                 ) |                 ) | ||||||
|         else: |         else: | ||||||
|             await query.edit_message_text( |             await query.edit_message_text( | ||||||
|                 f"On {chosen_match.date_pretty}, " |                 message_text, | ||||||
|                 f"{chosen_match.author} wrote: \n" |                 parse_mode=ParseMode.HTML | ||||||
|                 f"{chosen_match.text}" |  | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|         return ConversationHandler.END |         return ConversationHandler.END | ||||||
|   | |||||||
| @@ -4,18 +4,16 @@ import socket | |||||||
| from telegram.ext import ConversationHandler, CommandHandler, CallbackQueryHandler | from telegram.ext import ConversationHandler, CommandHandler, CallbackQueryHandler | ||||||
| from telegram import InlineKeyboardButton, InlineKeyboardMarkup | from telegram import InlineKeyboardButton, InlineKeyboardMarkup | ||||||
| from telegram.constants import ParseMode | from telegram.constants import ParseMode | ||||||
|  | import os | ||||||
|  |  | ||||||
|  |  | ||||||
| FIRST = 1 | FIRST = 1 | ||||||
| from .basehandler import BaseHandler | from .basehandler import BaseHandler | ||||||
| class StatusHandler(BaseHandler): | class StatusHandler(BaseHandler): | ||||||
|     """Shows a short status of the program.""" |     """Shows a short status of the program.""" | ||||||
|      |      | ||||||
|     def __init__(self, entry_string, models): |     def __init__(self, entry_string): | ||||||
|         self.start_time = datetime.datetime.now() |         self.start_time = datetime.datetime.now() | ||||||
|         self.entry_string = entry_string |         self.entry_string = entry_string | ||||||
|         self.models = models |  | ||||||
|         self.handler = ConversationHandler( |         self.handler = ConversationHandler( | ||||||
|             entry_points=[CommandHandler(self.entry_string, self.entry_point)], |             entry_points=[CommandHandler(self.entry_string, self.entry_point)], | ||||||
|             states={ |             states={ | ||||||
| @@ -50,9 +48,11 @@ class StatusHandler(BaseHandler): | |||||||
|             local_ips = "not fetchable" |             local_ips = "not fetchable" | ||||||
|  |  | ||||||
|         message += "Status: Running 🟢\n" |         message += "Status: Running 🟢\n" | ||||||
|  |         message += f"Version: `{os.getenv('BOT_VERSION', 'dev')}`\n" | ||||||
|         message += f"Uptime: `{delta[:delta.rfind('.')]}`\n" |         message += f"Uptime: `{delta[:delta.rfind('.')]}`\n" | ||||||
|         message += f"IP \(public\): `{ip}`\n" |         message += f"IP \(public\): `{ip}`\n" | ||||||
|         message += f"IP \(private\): `{local_ips}`\n" |         message += f"IP \(private\): `{local_ips}`\n" | ||||||
|  |         message += f"Chat ID: `{update.effective_chat.id}`\n" | ||||||
|  |  | ||||||
|         if update.message: |         if update.message: | ||||||
|             await update.message.reply_text(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2) |             await update.message.reply_text(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2) | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| import os | import os | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler | from telegram.ext import MessageHandler, filters | ||||||
| from telegram import InlineKeyboardButton, InlineKeyboardMarkup |  | ||||||
| from telegram import Update | from telegram import Update | ||||||
| import re | import re | ||||||
| import random | import random | ||||||
| @@ -18,30 +17,40 @@ TURTLE_VIDEOS = list(TURTLE_VIDEO_LOCATION.glob("*.mp4")) | |||||||
| class TurtleHandler(BaseHandler): | class TurtleHandler(BaseHandler): | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self.entry_string = "Variation of hallo" |         self.entry_string = "Variation of hallo" | ||||||
|         self.handler = MessageHandler(filters.Regex(r"[hH]([aA]+|[eE]+)[lL]{2,}[oOöÖ]+(le)?|(chen)") |  # react to hello strings |         self.handler = MessageHandler( | ||||||
|                                       filters.Regex(b"\xF0\x9F\x90\xA2".decode("utf8")) |               # react to turtle emoji |             filters.Regex(r"[hH]([aA]+|[eE]+)[lL]{2,}[oOöÖ]+(le|chen)?") | | ||||||
|                                       filters.Regex(r"[sS](childkröte)|[tT](urtle)"),                   # react to turtle string |             # react to hello strings | ||||||
|                                       self.entry_point) |             filters.Regex(b"\xF0\x9F\x90\xA2".decode("utf8")) | | ||||||
|         pass |             # react to turtle emoji | ||||||
|  |             filters.Regex(r"[sS](childkröte)|[tT](urtle)"), | ||||||
|  |             # react to turtle string | ||||||
|  |             self.entry_point | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def entry_point(self, update: Update, context): |     async def entry_point(self, update: Update, context): | ||||||
|         await super().entry_point(update, context) |         await super().entry_point(update, context) | ||||||
|         msgtxt = update.message.text |         msgtxt = update.message.text | ||||||
|         turtle_emoji = b"\xF0\x9F\x90\xA2".decode("utf8") |         turtle_emoji = b"\xF0\x9F\x90\xA2".decode("utf8") | ||||||
|  |  | ||||||
|         if "hallo" in msgtxt:                                               # react to hallo |         if "hallo" in msgtxt: | ||||||
|  |             # react to hallo | ||||||
|             vid = TURTLE_VIDEOS[0] |             vid = TURTLE_VIDEOS[0] | ||||||
|             answertxt = "Hallo!" |             answertxt = "Hallo!" | ||||||
|         elif re.search("[eE][lL]{2,}[oO]", msgtxt):                         # react to hello |         elif re.search("[eE][lL]{2,}[oO]", msgtxt): | ||||||
|  |             # react to hello | ||||||
|             vid = TURTLE_VIDEOS[2] |             vid = TURTLE_VIDEOS[2] | ||||||
|             answertxt = "Hello!"         |             answertxt = "Hello!"         | ||||||
|         elif re.search("([aA]{4,}|[lL]{4,}|[oO]{4,}|[öÖ]{4,})", msgtxt):    # react to stretched hello |         elif re.search("([aA]{4,}|[lL]{4,}|[oO]{4,}|[öÖ]{4,})", msgtxt): | ||||||
|  |             # react to stretched hello | ||||||
|             vid = TURTLE_VIDEOS[5] |             vid = TURTLE_VIDEOS[5] | ||||||
|             answertxt = "That's a lot of letters!" |             answertxt = "That's a lot of letters!" | ||||||
|         elif re.search(turtle_emoji, msgtxt):                               # react to turtle emoji |         elif re.search(turtle_emoji, msgtxt): | ||||||
|             vid=TURTLE_VIDEOS[0] # TODO: choose video for smiley reaction |             # react to turtle emoji | ||||||
|  |             vid=TURTLE_VIDEOS[0] | ||||||
|             answertxt="Turtle detected! Self-destruction mode activated..." |             answertxt="Turtle detected! Self-destruction mode activated..." | ||||||
|         elif re.search("[sS](childkröte)|[tT](urtle)", msgtxt):             # react to turtle string |         elif re.search("[sS](childkröte)|[tT](urtle)", msgtxt): | ||||||
|  |             # react to turtle string | ||||||
|             vid=None |             vid=None | ||||||
|             answertxt=turtle_emoji |             answertxt=turtle_emoji | ||||||
|         else: |         else: | ||||||
|   | |||||||
| @@ -1,22 +1,45 @@ | |||||||
| import os | import os | ||||||
| from pathlib import Path |  | ||||||
| from telegram.ext import ExtBot | from telegram.ext import ExtBot | ||||||
| import random | from telegram.error import BadRequest | ||||||
|  | import logging | ||||||
|  | from datetime import time, timedelta, timezone, datetime, date | ||||||
|  | from peewee import fn | ||||||
|  | import models | ||||||
|  |  | ||||||
| MEDIA_DIR = Path(os.getenv("MEDIA_DIR")) |  | ||||||
| CHAT_ID = os.getenv("CHAT_ID") | CHAT_ID = os.getenv("CHAT_ID") | ||||||
|  |  | ||||||
|  |  | ||||||
| async def set_random(bot: ExtBot) -> None: | class SetChatPhotoJob(): | ||||||
|     """Set a random chat photo.""" |     def __init__(self, bot: ExtBot, job_queue): | ||||||
|     if os.getenv("DOCKERIZED", "false") == "false": |         self.bot = bot | ||||||
|         # only change image on prod |         self.logger = logging.getLogger(self.__class__.__name__) | ||||||
|  |          | ||||||
|  |         if os.getenv("DOCKERIZED", "false") != "true": | ||||||
|  |             # when running locally, annoy the programmer every 60 seconds <3 | ||||||
|  |             job_queue.run_repeating(self.callback_photo, interval=60) | ||||||
|  |         else: | ||||||
|  |             # set the message sending time; include UTC shift +2 | ||||||
|  |             sending_time = time(hour=12, minute=0, second=0, tzinfo=timezone(timedelta(hours=2))) | ||||||
|  |             job_queue.run_monthly(self.callback_photo, when=sending_time, day=-1) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     async def callback_photo(self, context): | ||||||
|  |          | ||||||
|  |         # last_seen of memory must be older than 10 days in past or None | ||||||
|  |         with models.db: | ||||||
|  |             possible_photos = models.JournalEntry.select().where( | ||||||
|  |                 models.JournalEntry.media_path != None | ||||||
|  |             ).order_by(fn.Random()) | ||||||
|  |          | ||||||
|  |             try: | ||||||
|  |                 chosen_entry = possible_photos.get() | ||||||
|  |             except: | ||||||
|  |                 self.logger.warning("No photos available.") | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|     photos = list(MEDIA_DIR.glob("*.jpg")) + list(MEDIA_DIR.glob("*.png")) + list(MEDIA_DIR.glob("*.jpeg")) |         chat_id = os.getenv("CHAT_ID") | ||||||
|      |         try: | ||||||
|     if len(photos) == 0: |             await self.bot.set_chat_photo(chat_id, chosen_entry.media_path) | ||||||
|  |         except BadRequest: | ||||||
|  |             self.logger.error("This is a private chat!") | ||||||
|             return |             return | ||||||
|  |  | ||||||
|     photo = random.choice(photos) |  | ||||||
|     await bot.set_chat_photo(CHAT_ID, photo) |  | ||||||
|   | |||||||
							
								
								
									
										65
									
								
								bot/cronjob/random_memory.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								bot/cronjob/random_memory.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | from datetime import time, timedelta, timezone, datetime, date | ||||||
|  | from telegram.constants import ParseMode | ||||||
|  | import os | ||||||
|  | from peewee import fn | ||||||
|  | import logging | ||||||
|  | import models | ||||||
|  |  | ||||||
|  | class RandomMemoryJob(): | ||||||
|  |     def __init__(self, bot, job_queue): | ||||||
|  |         self.bot = bot | ||||||
|  |         self.logger = logging.getLogger(self.__class__.__name__) | ||||||
|  |          | ||||||
|  |         if os.getenv("DOCKERIZED", "false") != "true": | ||||||
|  |             # when running locally, annoy the programmer every 60 seconds <3 | ||||||
|  |             job_queue.run_repeating(self.callback_memory, interval=3600) | ||||||
|  |             self.min_age = 0 # do not filter messages: show them all | ||||||
|  |         else: | ||||||
|  |             # set the message sending time; include UTC shift +2 | ||||||
|  |             sending_time = time(hour=12, minute=0, second=0, tzinfo=timezone(timedelta(hours=2))) | ||||||
|  |             job_queue.run_daily(self.callback_memory, sending_time) | ||||||
|  |             self.min_age = 30 # days | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     async def callback_memory(self, context): | ||||||
|  |          | ||||||
|  |         # last_seen of memory must be older than 10 days in past or None | ||||||
|  |         with models.db: | ||||||
|  |             possible_entries = models.JournalEntry.select().where( | ||||||
|  |                 (models.JournalEntry.last_shown <= datetime.today().date() - timedelta(days=self.min_age)) | \ | ||||||
|  |                 (models.JournalEntry.last_shown == None) | ||||||
|  |             ).order_by(fn.Random()) | ||||||
|  |          | ||||||
|  |             try: | ||||||
|  |                 chosen_entry = possible_entries.get() | ||||||
|  |             except: | ||||||
|  |                 self.logger.warning("Come back later for another memory.") | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             # update the last_shown of the chosen entry | ||||||
|  |             chosen_entry.last_shown = datetime.today().date() | ||||||
|  |             chosen_entry.save() | ||||||
|  |  | ||||||
|  |         chat_id = os.getenv("CHAT_ID") | ||||||
|  |  | ||||||
|  |         rating_string = f" ({models.RATING_MAPPING[chosen_entry.rating]})" if chosen_entry.rating else "" | ||||||
|  |  | ||||||
|  |         message_text = f"On {chosen_entry.date_pretty}{rating_string}, " \ | ||||||
|  |                 f"{chosen_entry.author} wrote: \n" \ | ||||||
|  |                 f"{chosen_entry.spoiler_text}" | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         if chosen_entry.media_path: | ||||||
|  |             await self.bot.send_photo( | ||||||
|  |                 chat_id = chat_id, | ||||||
|  |                 photo = chosen_entry.media_path, | ||||||
|  |                 caption = message_text, | ||||||
|  |                 parse_mode=ParseMode.HTML | ||||||
|  |                 ) | ||||||
|  |         else: | ||||||
|  |             await self.bot.send_message( | ||||||
|  |                 chat_id = chat_id, | ||||||
|  |                 text = message_text, | ||||||
|  |                 parse_mode=ParseMode.HTML | ||||||
|  |             ) | ||||||
|  |  | ||||||
							
								
								
									
										23
									
								
								bot/main.py
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								bot/main.py
									
									
									
									
									
								
							| @@ -5,14 +5,13 @@ import logging | |||||||
| import models | import models | ||||||
| from commands import journal, status, turtle, memory, advent | from commands import journal, status, turtle, memory, advent | ||||||
| from commands.list import list | from commands.list import list | ||||||
| from cronjob import chat_photo | from cronjob import chat_photo, random_memory | ||||||
|  |  | ||||||
| logging.basicConfig( | logging.basicConfig( | ||||||
|     format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", |     format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", | ||||||
|     level=logging.INFO |     level=logging.INFO | ||||||
| ) | ) | ||||||
| import asyncio | logging.getLogger("httpx").setLevel(logging.WARNING) | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -24,22 +23,16 @@ def main() -> None: | |||||||
|     models.set_db(db_path) |     models.set_db(db_path) | ||||||
|     application = Application.builder().token(token).build() |     application = Application.builder().token(token).build() | ||||||
|  |  | ||||||
|  |     application.add_handler(journal.JournalHandler("journal").handler) | ||||||
|     application.add_handler(journal.JournalHandler("journal", models).handler) |     application.add_handler(list.ListHandler("list").handler) | ||||||
|     application.add_handler(list.ListHandler("list", models).handler) |     application.add_handler(status.StatusHandler("status").handler) | ||||||
|     application.add_handler(status.StatusHandler("status", models).handler) |  | ||||||
|     application.add_handler(turtle.TurtleHandler().handler) |     application.add_handler(turtle.TurtleHandler().handler) | ||||||
|     application.add_handler(memory.MemoryHandler("memory", models).handler) |     application.add_handler(memory.MemoryHandler("memory").handler) | ||||||
|     application.add_handler(advent.AdventsHandler("advent").handler) |     application.add_handler(advent.AdventsHandler("advent").handler) | ||||||
|  |  | ||||||
|     # application.add_handler(CommandHandler("help", help_command)) |     random_memory.RandomMemoryJob(application.bot, application.job_queue) | ||||||
|     # on non command i.e message - echo the message on Telegram |     chat_photo.SetChatPhotoJob(application.bot, application.job_queue) | ||||||
|     # application.add_handler(InlineQueryHandler(inline_query)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # on every start set a new chat photo |  | ||||||
|     # loop = asyncio.get_event_loop() |  | ||||||
|     asyncio.ensure_future(chat_photo.set_random(application.bot)) |  | ||||||
|     # Run the bot until the user presses Ctrl-C |     # Run the bot until the user presses Ctrl-C | ||||||
|     application.run_polling() |     application.run_polling() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| from peewee import * | from peewee import * | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
|  | import re | ||||||
| import os | import os | ||||||
| import datetime | import datetime | ||||||
|  |  | ||||||
| @@ -9,6 +10,14 @@ ID_MAPPINGS = { | |||||||
| } | } | ||||||
| ID_MAPPINGS_REV = dict((v, k) for k, v in ID_MAPPINGS.items()) | ID_MAPPINGS_REV = dict((v, k) for k, v in ID_MAPPINGS.items()) | ||||||
|  |  | ||||||
|  | RATING_MAPPING = { | ||||||
|  |     1: "😵", | ||||||
|  |     2: "☹️", | ||||||
|  |     3: "😐", | ||||||
|  |     4: "😃", | ||||||
|  |     5: "🥰" | ||||||
|  | } | ||||||
|  |  | ||||||
| MEDIA_DIR = Path(os.getenv("MEDIA_DIR")) | MEDIA_DIR = Path(os.getenv("MEDIA_DIR")) | ||||||
| MEDIA_DIR.mkdir(parents=True, exist_ok=True) | MEDIA_DIR.mkdir(parents=True, exist_ok=True) | ||||||
|  |  | ||||||
| @@ -27,7 +36,7 @@ class JournalEntry(BaseModel): | |||||||
|     text = TextField(null=True) |     text = TextField(null=True) | ||||||
|     media_path = TextField(null=True) |     media_path = TextField(null=True) | ||||||
|     last_shown = DateField(null=True) |     last_shown = DateField(null=True) | ||||||
|  |     rating = IntegerField(null=True) # mapped by RATING_MAPPING | ||||||
|      |      | ||||||
|     @property |     @property | ||||||
|     def media(self): |     def media(self): | ||||||
| @@ -60,6 +69,25 @@ class JournalEntry(BaseModel): | |||||||
|         except ValueError: #fck windows |         except ValueError: #fck windows | ||||||
|             return self.date.strftime('%a, %d. %b %Y') |             return self.date.strftime('%a, %d. %b %Y') | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def spoiler_text(self) -> str: | ||||||
|  |         """Returns the text with all the frisky details hidden away""" | ||||||
|  |         new_text = self.text.replace("<", "<").replace(">", ">").replace("&", "&") | ||||||
|  |         pattern = re.compile( | ||||||
|  |             "(" | ||||||
|  |             "(((?<=(\.|\!|\?)\s)[A-Z])|(^[A-Z]))" # beginning of a sentence | ||||||
|  |             "([^\.\!\?])+" # any character being part of a sentence | ||||||
|  |             "((\:\))|😇|😈|[Ss]ex)" # the smiley | ||||||
|  |             "([^\.\!\?])*" # continuation of sentence | ||||||
|  |             "(\.|\!|\?|\,|$)" # end of the sentence | ||||||
|  |             ")" | ||||||
|  |         ) | ||||||
|  |         matches = pattern.findall(new_text) | ||||||
|  |         for match in matches: | ||||||
|  |             group_to_replace = match[0] | ||||||
|  |             new_text = new_text.replace(group_to_replace, f"<tg-spoiler>{group_to_replace}</tg-spoiler>") | ||||||
|  |         return new_text | ||||||
|  |          | ||||||
|  |  | ||||||
| def set_db(db_path): | def set_db(db_path): | ||||||
|     db.initialize(SqliteDatabase(db_path)) |     db.initialize(SqliteDatabase(db_path)) | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| apiVersion: apps/v1 | apiVersion: apps/v1 | ||||||
| kind: Deployment | kind: Deployment | ||||||
| metadata: | metadata: | ||||||
|   namespace: journal |  | ||||||
|   name: journal-bot |   name: journal-bot | ||||||
|   labels: |   labels: | ||||||
|     app: journal-bot |     app: journal-bot | ||||||
| @@ -18,8 +17,7 @@ spec: | |||||||
|     spec: |     spec: | ||||||
|       containers: |       containers: | ||||||
|         - name: journal |         - name: journal | ||||||
|           image: mollre/journal-bot:latest |           image: mollre/journal-bot:1.0.19 | ||||||
|           imagePullPolicy: Always |  | ||||||
|           envFrom: |           envFrom: | ||||||
|             - secretRef: |             - secretRef: | ||||||
|                 name: journal-secret-env |                 name: journal-secret-env | ||||||
| @@ -39,12 +37,9 @@ spec: | |||||||
| apiVersion: v1 | apiVersion: v1 | ||||||
| kind: PersistentVolume | kind: PersistentVolume | ||||||
| metadata: | metadata: | ||||||
|   namespace: journal |  | ||||||
|   name: "journal-data-nfs" |   name: "journal-data-nfs" | ||||||
|   # labels: |  | ||||||
|   #   directory: "journal-data" |  | ||||||
| spec: | spec: | ||||||
|   storageClassName: fast |   storageClassName: "" | ||||||
|   capacity: |   capacity: | ||||||
|     storage: "5Gi" |     storage: "5Gi" | ||||||
|   accessModes: |   accessModes: | ||||||
| @@ -52,21 +47,17 @@ spec: | |||||||
|   nfs: |   nfs: | ||||||
|     path: /export/kluster/journal-bot |     path: /export/kluster/journal-bot | ||||||
|     server: 192.168.1.157 |     server: 192.168.1.157 | ||||||
|  |  | ||||||
| --- | --- | ||||||
| apiVersion: v1 | apiVersion: v1 | ||||||
| kind: PersistentVolumeClaim | kind: PersistentVolumeClaim | ||||||
| metadata: | metadata: | ||||||
|   namespace: journal |  | ||||||
|   name: "journal-data-nfs" |   name: "journal-data-nfs" | ||||||
| spec: | spec: | ||||||
|   storageClassName: "fast" |   storageClassName: "" | ||||||
|   accessModes: |   accessModes: | ||||||
|     - ReadWriteOnce |     - ReadWriteOnce | ||||||
|   resources: |   resources: | ||||||
|     requests: |     requests: | ||||||
|       storage: "5Gi" |       storage: "5Gi" | ||||||
|   # selector: |   volumeName: journal-data-nfs | ||||||
|   #   matchLabels: |  | ||||||
|   #     directory: "journal-data" |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								deployment/kustomization.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								deployment/kustomization.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | apiVersion: kustomize.config.k8s.io/v1beta1 | ||||||
|  | kind: Kustomization | ||||||
|  | resources: | ||||||
|  | - ./namespace.yaml | ||||||
|  | - ./deployment.yaml | ||||||
|  | - ./sealedsecret.yaml | ||||||
|  | images: | ||||||
|  | - name: mollre/journal-bot | ||||||
|  |   newTag: 1.0.68 | ||||||
							
								
								
									
										6
									
								
								deployment/namespace.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								deployment/namespace.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | apiVersion: v1 | ||||||
|  | kind: Namespace | ||||||
|  | metadata: | ||||||
|  |   name: journal | ||||||
|  |   labels: | ||||||
|  |     name: journal | ||||||
							
								
								
									
										25
									
								
								deployment/sealedsecret.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								deployment/sealedsecret.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | { | ||||||
|  |   "kind": "SealedSecret", | ||||||
|  |   "apiVersion": "bitnami.com/v1alpha1", | ||||||
|  |   "metadata": { | ||||||
|  |     "name": "journal-secret-env", | ||||||
|  |     "namespace": "journal", | ||||||
|  |     "creationTimestamp": null | ||||||
|  |   }, | ||||||
|  |   "spec": { | ||||||
|  |     "template": { | ||||||
|  |       "metadata": { | ||||||
|  |         "name": "journal-secret-env", | ||||||
|  |         "namespace": "journal", | ||||||
|  |         "creationTimestamp": null | ||||||
|  |       }, | ||||||
|  |       "type": "Opaque" | ||||||
|  |     }, | ||||||
|  |     "encryptedData": { | ||||||
|  |       "BOT_TOKEN": "AgBcFTg4wRI37/sXlk7bNO3IB9dC2CaVi/Oh9TQeQ/wN+rRkTTqp/dBpzX3Y1Zcp16d59AqT7y3DafGGZ/V87zcxG1bngdCZUsZfDmZMP0+z+10caMxjbSY4xfBW1/MEL3rW6ONOibhTAI9DDS3p3YCu1V92xRLvOUkwc+mCkV7fneWWGU/wgeci+C75PKTyGIilo5NZXROOyytN7BQwvOiY70j9m/NC9L0Ulbcppho9iuVsYVfkkWHOU6/OcOeL7vjWcvYFTleyI0oclLgtBUSJzBTqe5eJeZPGoVWMGwyMw2BqR8DgeGpDIuSnMEgssh9wUlhVvqkoI7CEUrJy5Rb2YnWcriIbfUfUMwbfc4EpBKt1VVlQcEQmN9jJdoOJ81ywRXl6CJTMMM4apB3iHsBWdzVXCG4I5c6Mv8+xg0V+AYdDt4pGwuRX9s66LShnjFJKnn68chNLnGfNlU68YdwFio0GJkV0/FnXIgTrOwdOovtBz1Gl3ORIWTmSkY7yBRyYBzvzEBlXBAuj51yhdykrmuW6B5CvwqXsm3ia1fvtWwNfvmKySjzbHZQHmbbYR9hLvWm+rPS7TFXw52W6jUyvHh6U2mSHwpwI2byIE+uhXjVQgYbmDgJW2gif3Aam+2VSaGSqWUz7ECSGy4mVC755CyoZl0HDP4PxuHq5kcGm34qmjTRNYM6Y2QvaVb7cBplsZfs+cH+gcfVu2gg3KvAEaFJk85Hz4pirRhcPsLNce6Iw5OegCZ5IQBrzOJXC", | ||||||
|  |       "CHAT_ID": "AgDIgpsygMIcsTDy8a49isS4Hfkmqa0oav+q7Mu7VtcPyrZ4o7hR1u/IlH8Qt4Cg/9QxOw7rJ4DfbK4GDmiOO1oOf0uaR6btLl+/GoKT3mbSHusWfHPrJDGX0SBFw7rOopC+LyFgDHPJEhbKviwnyrBkUuI6gnf1sic4jJ9arb/B97y89dMKFlVCbEzRCrTCK6WDBQ3Lpk+5MI+ugAPSKC8CqsjNc6jmWymdGMk/9n3sAdalfYBCucxHKeVgkrv4sPr9jEUEzIziKansavTs8qVbZgSUMEAAob2KBIAXLcRmo5ISwKvppuA6DMbbXEYEMHVJH9B4gI2eAxClOPVEOBElL+BtsJaSJnJbVEclMzOqwxXQRFPOq4BKxhguA+Uj8Vl8/2diwXEJoUiCZ5emGVvCFQd+Dr2LUPj4AO1AL4zAg+VahuqNV2gI/Dkxgt7Hj+i6jY/jmbk3MIJYjeZh0irmfsWQmUMcmizhxutQdV3kXhHSlomVDHuIdFHFIbjQrI8vSgeysQARSxrJZvt/qeNUNnD1InKa/EQ8I5XDX4o4qIV/pqY8XLVoTcciYDOPZEy3OleHK+26SJDkJOiDOAHbfIBeinaLvYIEW3BwgrakBD16HaNzYBPLPW2ikDCSBTyFRayvfkWHHUGawhdrauxvZzp5UsJViZogypBJuDT3SPvAOlAR4X8Yfr9SmwQYKv8rALH+wW7QtON6R9D7UA==", | ||||||
|  |       "DB_PATH": "AgACucJGoBiO/ymyf6FvEeuQ2MDo+c+VgNk30xY2EZSQZpAbNE6VbaMO0lZj++T82OOBnhfmXID+TWJTIwIHaD2nkPq/ISppBIobVmtUmsR+sm76tao02HJCtPGycyCu1zhEpKtwy3k5nsd0jclq4bQFHccnBaBdZ6xcvmevvJ+YddysHhjSb6ESOoah/5lGiAa3sHe1Hwg57FPZeVuZOCx+MbbmhAXCYbu2bdZzSWA/mFAf1F6qkxSAuVFMJtijPrM13UIVONBPg5E02NO1VEV+LkI5NTUNx9YHVlGNPxogabDq+lhNwulWtLPbKkAXn/CTgcB2vb03geeVN20yz1UzbrdV5CKpIGGZ1At8ehuNypFa595XBVFSmL8RWNmfHCAvypAypjxOMWa89qC0diJW4tY+BUrl2jrIXpARNlD2GTqp7InLtwFsFb3AUbpg+0mnqqiqmtqYKQWJj0WDvfs7ol6k6Qx7P0FdAfRq3ojzArRQ7MruI24CS1hGTQ/hBJaHrcMzEIn/ZkKqZ2fEdNkJt8tTfxVsZ/paE97ERfYeKsuK2uHqUgvSQ+0w7FK7m6PYNpY9gmqZMaqRA7VIRblNauEuLDAGw8Jgyb3YUiV3xLjBuvaCaAEbXOsNsIF35+ZCw2bsYmgSxqNO2b1LvW/uFSUonjVJ9yT9+Qv6V0YJhMztzDLpNKRIEBKgyUN/BE7hV9vtygaan/AkUVmHVA9BwrU=", | ||||||
|  |       "PERSISTENCE_DIR": "AgDF8D5PfJ4DF7wNd8hpEPdph3r/MvK8R/sREX83/b7jFJgSGb/Dku82vwtkDTzsk9A+gGgU//EILYt5Irlo5ObHlcKfimR4fTvdcO9lpBHJnbwXipUcg20Xz/awIGbD3yJmy0LJqgc5MVPtWicF/ZsxQkkcv3+DN4+BgXczncEL+3g55eEbCAwbmFSvdHWIZGbi04VmtmAlSduPVzi7nqH05Nslqtu4p5mZaxPHjGvna0DcIVQNFQC/Wgobox8pezeJ6tANUPFAUpJUp+E5N3q8DofZXiHBQTpNXo3tyM2JYT/IwovSIdPSgJeIhbrfP6hVKOeZjKqaC8/SV67R/1LUpdO7KeKig5d7LJa3vjeeDmrM/3+1vb0C96Xrgv39j4MyPx7HrZxFdOQif4PZlpLEfarEtuSFUAUXx4N2uhLbTyAXyl4dfGkqdbQ5O/UT6xxXw44JsK6DzOz5OkT6cB0uUvJa5TrHk+cVoxlUu3Ex/5o6KXnMFaxfzyizPXeiIs/mTT9Bq6nAtvGad84U6Wvua3ZPofOlH0gFyN8/uMJxwqXbKuO1iScjkxuCNX8YRFzcoWH7VXzbbNPIQLORTu9/PhAIRZXOSxZw2iPZVg3LKuyyW3MgTtnVvgrKYnynw1Yrvc7gu35MhwjvnRrPlVQ+yFrZuB3l2Cf0OWZwonlMr36TsPHIJq+wD4ZEja+ciOsRAWzWuxBEDQ==" | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								renovate.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								renovate.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | { | ||||||
|  |   "$schema": "https://docs.renovatebot.com/renovate-schema.json", | ||||||
|  |   "packageRules": [ | ||||||
|  |     { | ||||||
|  |       "matchUpdateTypes": ["minor", "patch"], | ||||||
|  |       "matchCurrentVersion": "!/^0/", | ||||||
|  |       "automerge": true, | ||||||
|  |       "automergeType": "branch" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "commitMessagePrefix" : "[CI SKIP]" | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user