Compare commits
No commits in common. "main" and "secretbranch" have entirely different histories.
main
...
secretbran
@ -16,10 +16,7 @@ steps:
|
||||
from_secret: docker_pw
|
||||
|
||||
repo: mollre/journal-bot
|
||||
tags:
|
||||
- 1.0.${DRONE_BUILD_NUMBER}
|
||||
- latest
|
||||
build_args: "BOT_VERSION=1.0.${DRONE_BUILD_NUMBER}"
|
||||
tags: latest
|
||||
|
||||
|
||||
trigger:
|
||||
|
9
.vscode/launch.json
vendored
9
.vscode/launch.json
vendored
@ -4,6 +4,15 @@
|
||||
// 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",
|
||||
|
@ -1,16 +1,11 @@
|
||||
FROM python:3-slim
|
||||
FROM python:3.10-slim
|
||||
ENV DOCKERIZED=true
|
||||
ARG BOT_VERSION
|
||||
# set at build time
|
||||
ENV BOT_VERSION=$BOT_VERSION
|
||||
|
||||
WORKDIR /app
|
||||
RUN pip install pipenv
|
||||
|
||||
|
||||
COPY Pipfile Pipfile.lock ./
|
||||
|
||||
RUN pipenv install --system --deploy
|
||||
RUN pip install pipenv && pipenv install --system --deploy
|
||||
|
||||
COPY bot .
|
||||
|
||||
|
6
Pipfile
6
Pipfile
@ -4,11 +4,7 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
python-telegram-bot = "*"
|
||||
peewee = "*"
|
||||
python-telegram-bot = {extras = ["job-queue"], version = "*"}
|
||||
anyio = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[pipenv]
|
||||
allow_prereleases = true
|
||||
|
77
Pipfile.lock
generated
77
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "5458e81c4f85af776acc44f46af838644ef8c00ccf4223fbe06f9d76a4717fc6"
|
||||
"sha256": "e95b9deab62bd0c661f20a178b8701fc84420db5f663fa4416666e1d05f6ce76"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
@ -16,26 +16,19 @@
|
||||
"default": {
|
||||
"anyio": {
|
||||
"hashes": [
|
||||
"sha256:48d53f0b141f5757c38d648309e6fe254857fae092d67f938fa248d7c0f36804",
|
||||
"sha256:596b09c520820e7eed961ddc889540972f92d5e8fcb081117fc054c409df34ae"
|
||||
"sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421",
|
||||
"sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.0.0rc1"
|
||||
},
|
||||
"apscheduler": {
|
||||
"hashes": [
|
||||
"sha256:0293937d8f6051a0f493359440c1a1b93e882c57daf0197afeff0e727777b96e",
|
||||
"sha256:e813ad5ada7aff36fb08cdda746b520531eaac7757832abc204868ba78e0c8f6"
|
||||
],
|
||||
"version": "==3.10.1"
|
||||
"markers": "python_full_version >= '3.6.2'",
|
||||
"version": "==3.6.2"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082",
|
||||
"sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"
|
||||
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
|
||||
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2023.7.22"
|
||||
"version": "==2022.12.7"
|
||||
},
|
||||
"h11": {
|
||||
"hashes": [
|
||||
@ -47,19 +40,19 @@
|
||||
},
|
||||
"httpcore": {
|
||||
"hashes": [
|
||||
"sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888",
|
||||
"sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87"
|
||||
"sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb",
|
||||
"sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.17.3"
|
||||
"version": "==0.16.3"
|
||||
},
|
||||
"httpx": {
|
||||
"hashes": [
|
||||
"sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd",
|
||||
"sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"
|
||||
"sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9",
|
||||
"sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.24.1"
|
||||
"version": "==0.23.3"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
@ -77,38 +70,22 @@
|
||||
"version": "==3.16.2"
|
||||
},
|
||||
"python-telegram-bot": {
|
||||
"extras": [
|
||||
"job-queue"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:a6ac3f9c9674aaf7d1c7e652d8b75cde969fb872f75e9521b8516eceaba82b1b",
|
||||
"sha256:e426404b0006989a5bcc05e11a7ef3ffe0c086b684a4e963db5bda1d361a049a"
|
||||
"sha256:4d1d4b643ce158aa17a0987b84005eaf25fe0ce8b38fd234099594985611c198",
|
||||
"sha256:d0aa53e1f06d7cb7919cc0e2d6c81a02d968fc29921aeaa962edd1efb816a9bd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==20.4"
|
||||
"version": "==20.2"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588",
|
||||
"sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"
|
||||
"rfc3986": {
|
||||
"extras": [
|
||||
"idna2008"
|
||||
],
|
||||
"version": "==2023.3"
|
||||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f",
|
||||
"sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"
|
||||
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
|
||||
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
|
||||
],
|
||||
"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"
|
||||
"version": "==1.5.0"
|
||||
},
|
||||
"sniffio": {
|
||||
"hashes": [
|
||||
@ -117,14 +94,6 @@
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"tzlocal": {
|
||||
"hashes": [
|
||||
"sha256:46eb99ad4bdb71f3f72b7d24f4267753e240944ecfc16f25d2719ba89827a803",
|
||||
"sha256:f3596e180296aaf2dbd97d124fe76ae3a0e3d32b258447de7b939b3fd4be992f"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==5.0.1"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
|
@ -1,9 +1,3 @@
|
||||
# journal-bot
|
||||
|
||||
Sharing memories, the digital way...
|
||||
|
||||
|
||||
## Migration 10.03.24
|
||||
```
|
||||
ALTER TABLE journalentry ADD COLUMN rating INTEGER;
|
||||
```
|
||||
Sharing memories, the digital way...
|
@ -2,20 +2,11 @@ from datetime import date
|
||||
from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler, CallbackContext
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, InputMediaPhoto
|
||||
import random
|
||||
import os
|
||||
from pathlib import Path
|
||||
# ACTION_CHOICE, DATE_ENTRY, ADD_CONTENT = range(3)
|
||||
ACTION, TUERCHEN_CHOICE = range(2)
|
||||
|
||||
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"))
|
||||
from .basehandler import BaseHandler
|
||||
|
||||
class AdventsHandler(BaseHandler):
|
||||
def __init__(self, entry_string):
|
||||
@ -75,11 +66,12 @@ 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")]]
|
||||
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
await update.message.reply_sticker(sticker=STICKER_LOCATION/"stickerwhat.tgs")
|
||||
await update.message.reply_sticker(sticker=open(".bot_storage\stickers\stickerwhat.tgs", "rb"))
|
||||
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
|
||||
|
||||
|
||||
async def kuesschen(self, update: Update, context: CallbackContext):
|
||||
query = update.callback_query
|
||||
await query.answer()
|
||||
@ -87,7 +79,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")]]
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
|
||||
await update.effective_message.reply_sticker(sticker=STICKER_LOCATION/"stickerkiss.tgs")
|
||||
await update.effective_message.reply_sticker(sticker=open(".bot_storage\stickers\stickerkiss.tgs", "rb"))
|
||||
await update.effective_message.reply_text(text="Mua!", reply_markup=reply_markup)
|
||||
|
||||
return ACTION
|
||||
@ -109,8 +101,7 @@ class AdventsHandler(BaseHandler):
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
|
||||
picture_number = random.randint(1,31)
|
||||
#print(picture_number-1)
|
||||
await update.effective_message.reply_photo(photo=PICTURES[picture_number-1], caption="So ein cutes Foto!", reply_markup=reply_markup)
|
||||
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)
|
||||
|
||||
return ACTION
|
||||
|
||||
@ -125,11 +116,11 @@ class AdventsHandler(BaseHandler):
|
||||
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
|
||||
datum_tuer_heute = date(2023, 12, tuer_nummer)
|
||||
if (date.today() - datum_tuer_heute).days >= 0:
|
||||
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)
|
||||
#if tuer_nummer <= int(date.today().strftime("%d")):
|
||||
if (tuer_nummer <= int(date.today().strftime("%d"))) and (int(date.today().strftime("%m"))==12):
|
||||
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)
|
||||
else:
|
||||
await update.message.reply_sticker(sticker=STICKER_LOCATION/"stickerangry.tgs")
|
||||
await update.message.reply_sticker(sticker=open(".bot_storage\stickers\stickerangry.tgs", "rb"))
|
||||
await update.message.reply_text(text="Hey, nicht schummeln! Dieses Türchen darfst du noch nicht sehen.", reply_markup=reply_markup)
|
||||
|
||||
return ACTION
|
||||
@ -138,7 +129,7 @@ class AdventsHandler(BaseHandler):
|
||||
query = update.callback_query
|
||||
await query.answer()
|
||||
|
||||
await update.effective_message.reply_sticker(sticker=STICKER_LOCATION/"stickerbye.tgs")
|
||||
await update.effective_message.reply_sticker(sticker=open(".bot_storage\stickers\stickerbye.tgs", "rb"))
|
||||
await update.effective_message.reply_text(text="Bye bye, Bubo! Hab dich ganz doll lieb! 😘")
|
||||
|
||||
return ConversationHandler.END
|
||||
|
@ -5,4 +5,4 @@ class BaseHandler:
|
||||
entry_string: str
|
||||
|
||||
async def entry_point(self, update, context) -> None:
|
||||
self.logger.info(f"Chat ({update.message.chat_id}) said: {self.entry_string}")
|
||||
self.logger.info(f"Chat said: {self.entry_string}")
|
@ -2,33 +2,28 @@ import datetime
|
||||
import os
|
||||
from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram.constants import ParseMode
|
||||
import models
|
||||
|
||||
ENTRY_OPTIONS, CONTENT_ENTRY, DAY_RATING = range(3)
|
||||
BUTTON_COUNT = 5
|
||||
|
||||
ACTION_CHOICE, DATE_ENTRY, ADD_CONTENT = range(3)
|
||||
|
||||
from .basehandler import BaseHandler
|
||||
|
||||
class JournalHandler(BaseHandler):
|
||||
def __init__(self, entry_string):
|
||||
def __init__(self, entry_string, models):
|
||||
self.models = models
|
||||
self.entry_string = entry_string
|
||||
self.handler = ConversationHandler(
|
||||
entry_points=[CommandHandler(entry_string, self.entry_point)],
|
||||
states={
|
||||
ENTRY_OPTIONS: [
|
||||
CallbackQueryHandler(self.date_button, pattern=r"^\d{8}$"), # a serialized date
|
||||
CallbackQueryHandler(self.date_custom, pattern=r"^\d{1,3}$"), # a ~ small delta, symbolizing a new range to show
|
||||
CallbackQueryHandler(self.option_delete, pattern="delete"),
|
||||
ACTION_CHOICE: [
|
||||
CallbackQueryHandler(self.date_choice, pattern="today|yesterday"),
|
||||
CallbackQueryHandler(self.date_custom, pattern="custom"),
|
||||
CallbackQueryHandler(self.option_delete, pattern="delete")
|
||||
],
|
||||
DATE_ENTRY: [
|
||||
MessageHandler(filters.ALL, self.date_entry),
|
||||
],
|
||||
CONTENT_ENTRY: [
|
||||
ADD_CONTENT: [
|
||||
MessageHandler(filters.ALL, self.content_save),
|
||||
],
|
||||
DAY_RATING: [
|
||||
CallbackQueryHandler(self.day_rating_save),
|
||||
],
|
||||
]
|
||||
},
|
||||
fallbacks=[],
|
||||
)
|
||||
@ -42,74 +37,52 @@ class JournalHandler(BaseHandler):
|
||||
await update.message.reply_text("You are not authorized to use this bot")
|
||||
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 = [
|
||||
[InlineKeyboardButton(n, callback_data=c)] for n,c in zip(names[::-1], callbacks[::-1])
|
||||
] + [
|
||||
[
|
||||
InlineKeyboardButton("<<", callback_data=BUTTON_COUNT + 2)
|
||||
options = [[
|
||||
InlineKeyboardButton("Today", callback_data="today"),
|
||||
InlineKeyboardButton("Yesterday", callback_data="yesterday"),
|
||||
InlineKeyboardButton("Custom date", callback_data="custom"),
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton("Delete", callback_data="delete")
|
||||
]
|
||||
]
|
||||
keyboard = InlineKeyboardMarkup(options)
|
||||
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 ENTRY_OPTIONS
|
||||
await update.message.reply_text("Please choose an option for the entry:", reply_markup=keyboard)
|
||||
return ACTION_CHOICE
|
||||
|
||||
|
||||
async def date_button(self, update, context):
|
||||
async def date_choice(self, update, context):
|
||||
query = update.callback_query
|
||||
await query.answer()
|
||||
date = datetime.datetime.strptime(query.data, "%d%m%Y").date()
|
||||
if query.data == "today":
|
||||
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 models.db:
|
||||
self.current_model, new = models.JournalEntry.get_or_create(
|
||||
with self.models.db:
|
||||
self.current_model, new = self.models.JournalEntry.get_or_create(
|
||||
date = date
|
||||
)
|
||||
if new:
|
||||
count = models.JournalEntry.select().count()
|
||||
await query.edit_message_text(
|
||||
text=f"Journal entry no. {count}. What happened on {self.current_model.date_pretty}?"
|
||||
text=f"What is your entry for {self.current_model.date_pretty}?"
|
||||
)
|
||||
else:
|
||||
await query.edit_message_text(text="An entry already exists for this date")
|
||||
return ConversationHandler.END
|
||||
|
||||
return CONTENT_ENTRY
|
||||
return ADD_CONTENT
|
||||
|
||||
|
||||
async def date_custom(self, update, context):
|
||||
query = update.callback_query
|
||||
await query.answer()
|
||||
delta = int(query.data)
|
||||
await query.edit_message_text(text="Please enter the date in the format DDMMYYYY")
|
||||
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):
|
||||
date = update.message.text
|
||||
@ -117,12 +90,12 @@ class JournalHandler(BaseHandler):
|
||||
try:
|
||||
date = datetime.datetime.strptime(date, "%d%m%Y").date()
|
||||
except ValueError:
|
||||
await update.message.reply_text("Please enter the date in the format _DDMMYYYY_", parse_mode=ParseMode.MARKDOWN_V2)
|
||||
return ENTRY_OPTIONS
|
||||
await update.message.reply_text("Please enter the date in the format DDMMYYYY")
|
||||
return DATE_ENTRY
|
||||
|
||||
if context.chat_data.get("delete", False): # if not set, delete was not chosen
|
||||
with models.db:
|
||||
self.current_model = models.JournalEntry.get_or_none(
|
||||
with self.models.db:
|
||||
self.current_model = self.models.JournalEntry.get_or_none(
|
||||
date = date
|
||||
)
|
||||
if self.current_model:
|
||||
@ -132,23 +105,20 @@ class JournalHandler(BaseHandler):
|
||||
context.chat_data["delete"] = False
|
||||
return ConversationHandler.END
|
||||
else:
|
||||
with models.db:
|
||||
self.current_model, new = models.JournalEntry.get_or_create(
|
||||
with self.models.db:
|
||||
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
|
||||
else:
|
||||
count = models.JournalEntry.select().count()
|
||||
await update.message.reply_text(
|
||||
text=f"Journal entry no. {count}. What happened on {self.current_model.date_pretty}?"
|
||||
)
|
||||
return CONTENT_ENTRY
|
||||
await update.message.reply_text(f"What is your entry for {self.current_model.date_pretty}?")
|
||||
return ADD_CONTENT
|
||||
|
||||
|
||||
async def content_save(self, update, context):
|
||||
with models.db:
|
||||
with self.models.db:
|
||||
self.current_model.author_id = update.message.from_user.id
|
||||
|
||||
if update.message.text:
|
||||
@ -164,58 +134,23 @@ class JournalHandler(BaseHandler):
|
||||
self.current_model.save_media(file_bytes, file_path)
|
||||
|
||||
self.current_model.text = update.message.caption
|
||||
|
||||
|
||||
self.current_model.save()
|
||||
|
||||
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 ✅")
|
||||
await update.message.reply_text(f"Saved entry ✅")
|
||||
return ConversationHandler.END
|
||||
|
||||
|
||||
async def option_delete(self, update, context):
|
||||
query = update.callback_query
|
||||
await query.answer()
|
||||
await query.edit_message_text(text="Please enter the date in the format _DDMMYYYY_", parse_mode=ParseMode.MARKDOWN_V2)
|
||||
await query.edit_message_text(text="Please enter the date in the format DDMMYYYY")
|
||||
context.chat_data["delete"] = True
|
||||
return ENTRY_OPTIONS
|
||||
return DATE_ENTRY
|
||||
|
||||
|
||||
async def delete_entry(self, update, context):
|
||||
with models.db:
|
||||
with self.models.db:
|
||||
self.current_model.delete_instance()
|
||||
context.chat_data["delete"] = False
|
||||
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
|
||||
await update.message.reply_text(text="Entry deleted ✅")
|
@ -5,7 +5,10 @@ from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
|
||||
from .models import ListModel, set_db, db
|
||||
|
||||
PERSISTENCE_DIR = Path(os.getenv("PERSISTENCE_DIR"))
|
||||
MEDIA_DIR = Path(os.getenv("MEDIA_DIR"))
|
||||
DB_DIR = MEDIA_DIR / "lists_db"
|
||||
DB_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
NAME, NEW, ACTION, ITEMADD, ITEMREMOVE, ITEMTOGGLE = range(6)
|
||||
|
||||
@ -15,17 +18,16 @@ from ..basehandler import BaseHandler
|
||||
class ListHandler(BaseHandler):
|
||||
"""Create and edit lists"""
|
||||
|
||||
def __init__(self, entry_string):
|
||||
def __init__(self, entry_string, models):
|
||||
self.journal_models = models # not needed here
|
||||
self.entry_string = entry_string
|
||||
|
||||
set_db(PERSISTENCE_DIR / "lists.sqlite")
|
||||
|
||||
self.list_overview_keyboard = [
|
||||
[InlineKeyboardButton("Print list", callback_data="print")],
|
||||
[InlineKeyboardButton("Add item", callback_data="add")],
|
||||
[InlineKeyboardButton("Toggle item", callback_data="toggle")],
|
||||
[InlineKeyboardButton("Remove item", callback_data="remove")],
|
||||
[InlineKeyboardButton("Clear list", callback_data="clear")],
|
||||
[InlineKeyboardButton("Print list", callback_data="print")],
|
||||
[InlineKeyboardButton("Delete list", callback_data="delete")],
|
||||
|
||||
]
|
||||
@ -38,13 +40,13 @@ class ListHandler(BaseHandler):
|
||||
],
|
||||
NEW : [MessageHandler(filters.TEXT, callback=self.new_listname)],
|
||||
ACTION: [
|
||||
CallbackQueryHandler(self.list_print, pattern="^print$"),
|
||||
CallbackQueryHandler(self.list_add, pattern="^add$"),
|
||||
CallbackQueryHandler(self.list_toggle, pattern="^toggle$"),
|
||||
CallbackQueryHandler(self.list_menu, pattern="^overview$"),
|
||||
CallbackQueryHandler(self.list_remove, pattern="^remove$"),
|
||||
CallbackQueryHandler(self.list_clear, pattern="^clear$"),
|
||||
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)],
|
||||
ITEMTOGGLE: [CallbackQueryHandler(self.list_toggle_index)],
|
||||
@ -56,9 +58,10 @@ class ListHandler(BaseHandler):
|
||||
|
||||
async def entry_point(self, update, context) -> None:
|
||||
await super().entry_point(update, context)
|
||||
set_db(DB_DIR / f"chat_{update.message.chat_id}.db")
|
||||
with db:
|
||||
lists = ListModel.select().where(ListModel.chat_id == update.effective_chat.id)
|
||||
keyboard = [[InlineKeyboardButton(k.name, callback_data=f"list-{k.id}")] for k in lists] + \
|
||||
lists = ListModel.select()
|
||||
keyboard = [[InlineKeyboardButton(k.name, callback_data=f"list-{k.name}")] for k in lists] + \
|
||||
[[InlineKeyboardButton("New list", callback_data="new")]]
|
||||
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
@ -69,13 +72,13 @@ class ListHandler(BaseHandler):
|
||||
async def choose_list(self, update, context: CallbackContext) -> None:
|
||||
query = update.callback_query
|
||||
data = query.data
|
||||
id = data.replace("list-","")
|
||||
name = data.replace("list-","")
|
||||
await query.answer()
|
||||
context.user_data["current_list"] = ListModel.get(id = id)
|
||||
context.user_data["current_list"] = ListModel.get(name = name)
|
||||
|
||||
reply_markup = InlineKeyboardMarkup(self.list_overview_keyboard)
|
||||
|
||||
await query.edit_message_text(f"Using {context.user_data['current_list'].name}. Available actions:", reply_markup=reply_markup)
|
||||
await query.edit_message_text("Very well. For " + name + " the following actions are available:", reply_markup=reply_markup)
|
||||
return ACTION
|
||||
|
||||
|
||||
@ -85,7 +88,7 @@ class ListHandler(BaseHandler):
|
||||
|
||||
reply_markup = InlineKeyboardMarkup(self.list_overview_keyboard)
|
||||
|
||||
await query.edit_message_text(f"Using {context.user_data['current_list'].name}. Available actions:", reply_markup=reply_markup)
|
||||
await query.edit_message_text(f"Very well. For {context.user_data['current_list'].name} the following actions are available:", reply_markup=reply_markup)
|
||||
return ACTION
|
||||
|
||||
|
||||
@ -100,7 +103,7 @@ class ListHandler(BaseHandler):
|
||||
name = update.message.text
|
||||
try:
|
||||
with db:
|
||||
context.user_data["current_list"] = ListModel.create(name = name, chat_id=update.effective_chat.id)
|
||||
context.user_data["current_list"] = ListModel.create(name = name)
|
||||
keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]]
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
await update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup)
|
||||
@ -122,17 +125,10 @@ class ListHandler(BaseHandler):
|
||||
await query.answer()
|
||||
|
||||
list_object = context.user_data["current_list"]
|
||||
readable_it = printable_list(list_object)
|
||||
|
||||
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
|
||||
keyboard = [[InlineKeyboardButton(v, callback_data=k)] for k,v in list_object.content.items()]
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
|
||||
await query.edit_message_text(msg_content, reply_markup = reply_markup)
|
||||
await query.edit_message_text("Which item would you like to toggle?", reply_markup = reply_markup)
|
||||
return ITEMTOGGLE
|
||||
|
||||
|
||||
@ -174,10 +170,14 @@ class ListHandler(BaseHandler):
|
||||
await query.answer()
|
||||
list_object = context.user_data["current_list"]
|
||||
|
||||
readable_it = printable_list(list_object)
|
||||
|
||||
if readable_it:
|
||||
msg_content = "\n".join(readable_it)
|
||||
content_it = list_object.content.values()
|
||||
done_it = [
|
||||
"· " if e is None \
|
||||
else "✅ " if e \
|
||||
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:
|
||||
msg_content = "List empty"
|
||||
|
||||
@ -193,6 +193,7 @@ class ListHandler(BaseHandler):
|
||||
new = list_object.content
|
||||
new.update({"random_key": item})
|
||||
list_object.content = new
|
||||
# TODO test me!
|
||||
keyboard = [[InlineKeyboardButton("Add some more", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
await update.message.reply_text(f"Added {item}", reply_markup=reply_markup)
|
||||
@ -205,10 +206,11 @@ class ListHandler(BaseHandler):
|
||||
await query.answer()
|
||||
|
||||
list_object = context.user_data["current_list"]
|
||||
old = list_object.done_dict[toggle_key] or False
|
||||
# if it was previously unset (None), we can later on set it to not old = True
|
||||
|
||||
new_done_dict = list_object.done_dict
|
||||
old = list_object.done_dict[toggle_key]
|
||||
# if all None or all False (first toggle or all false) then set all dones to False
|
||||
if not any(list_object.done_dict.values()):
|
||||
new_done_dict = dict.fromkeys(list_object.done_dict, False)
|
||||
else: new_done_dict = list_object.done_dict
|
||||
new_done_dict[toggle_key] = not old
|
||||
list_object.done_dict = new_done_dict
|
||||
|
||||
@ -234,22 +236,3 @@ class ListHandler(BaseHandler):
|
||||
|
||||
await query.edit_message_text(f"Removed {name}", reply_markup=reply_markup)
|
||||
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,7 +8,6 @@ class BaseModel(Model):
|
||||
|
||||
class ListModel(BaseModel):
|
||||
name = CharField(default="")
|
||||
chat_id = IntegerField()
|
||||
|
||||
@property
|
||||
def content(self) -> dict:
|
||||
@ -46,6 +45,22 @@ class ListEntryModel(BaseModel):
|
||||
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):
|
||||
db.initialize(SqliteDatabase(db_path))
|
||||
with db:
|
||||
|
@ -1,8 +1,6 @@
|
||||
import os
|
||||
import datetime
|
||||
from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler, CallbackContext
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, InputMediaPhoto
|
||||
import models
|
||||
from telegram.constants import ParseMode
|
||||
# ACTION_CHOICE, DATE_ENTRY, ADD_CONTENT = range(3)
|
||||
MEMORY_CHOICE = range(1)
|
||||
|
||||
@ -10,7 +8,8 @@ MEMORY_CHOICE = range(1)
|
||||
from .basehandler import BaseHandler
|
||||
|
||||
class MemoryHandler(BaseHandler):
|
||||
def __init__(self, entry_string):
|
||||
def __init__(self, entry_string, models):
|
||||
self.models = models
|
||||
self.entry_string = entry_string
|
||||
self.handler = ConversationHandler(
|
||||
entry_points=[CommandHandler(entry_string, self.entry_point, )],
|
||||
@ -28,27 +27,20 @@ class MemoryHandler(BaseHandler):
|
||||
|
||||
async def entry_point(self, update: Update, context: CallbackContext):
|
||||
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)
|
||||
|
||||
|
||||
if search_string == '~photo':
|
||||
matching_models = models.JournalEntry.select().where(models.JournalEntry.media_path != "").order_by(models.JournalEntry.date)
|
||||
matching_models = self.models.JournalEntry.select().where(self.models.JournalEntry.media_path != "").order_by(self.models.JournalEntry.date)
|
||||
else: # searching for text
|
||||
matching_models = models.JournalEntry.select().where(
|
||||
models.JournalEntry.text.contains(
|
||||
matching_models = self.models.JournalEntry.select().where(
|
||||
self.models.JournalEntry.text.contains(
|
||||
search_string
|
||||
)
|
||||
).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
|
||||
).order_by(self.models.JournalEntry.date)
|
||||
|
||||
|
||||
options = [[InlineKeyboardButton(m.date_pretty, callback_data=i)] for i,m in enumerate(matching_models)]
|
||||
|
||||
keyboard = InlineKeyboardMarkup(options)
|
||||
@ -69,23 +61,20 @@ class MemoryHandler(BaseHandler):
|
||||
matching_models = context.chat_data["kept_matches"]
|
||||
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:
|
||||
# context.bot.sendPhoto()
|
||||
await update.effective_message.reply_photo(
|
||||
photo = chosen_match.media_path,
|
||||
caption = message_text,
|
||||
parse_mode=ParseMode.HTML
|
||||
caption=
|
||||
f"On {chosen_match.date_pretty}, "
|
||||
f"{chosen_match.author} wrote: \n"
|
||||
f"{chosen_match.text}"
|
||||
)
|
||||
else:
|
||||
await query.edit_message_text(
|
||||
message_text,
|
||||
parse_mode=ParseMode.HTML
|
||||
f"On {chosen_match.date_pretty}, "
|
||||
f"{chosen_match.author} wrote: \n"
|
||||
f"{chosen_match.text}"
|
||||
)
|
||||
|
||||
return ConversationHandler.END
|
||||
|
@ -4,16 +4,18 @@ import socket
|
||||
from telegram.ext import ConversationHandler, CommandHandler, CallbackQueryHandler
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram.constants import ParseMode
|
||||
import os
|
||||
|
||||
|
||||
|
||||
FIRST = 1
|
||||
from .basehandler import BaseHandler
|
||||
class StatusHandler(BaseHandler):
|
||||
"""Shows a short status of the program."""
|
||||
|
||||
def __init__(self, entry_string):
|
||||
def __init__(self, entry_string, models):
|
||||
self.start_time = datetime.datetime.now()
|
||||
self.entry_string = entry_string
|
||||
self.models = models
|
||||
self.handler = ConversationHandler(
|
||||
entry_points=[CommandHandler(self.entry_string, self.entry_point)],
|
||||
states={
|
||||
@ -48,11 +50,9 @@ class StatusHandler(BaseHandler):
|
||||
local_ips = "not fetchable"
|
||||
|
||||
message += "Status: Running 🟢\n"
|
||||
message += f"Version: `{os.getenv('BOT_VERSION', 'dev')}`\n"
|
||||
message += f"Uptime: `{delta[:delta.rfind('.')]}`\n"
|
||||
message += f"IP \(public\): `{ip}`\n"
|
||||
message += f"IP \(private\): `{local_ips}`\n"
|
||||
message += f"Chat ID: `{update.effective_chat.id}`\n"
|
||||
|
||||
if update.message:
|
||||
await update.message.reply_text(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from telegram.ext import MessageHandler, filters
|
||||
from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram import Update
|
||||
import re
|
||||
import random
|
||||
@ -17,47 +18,37 @@ TURTLE_VIDEOS = list(TURTLE_VIDEO_LOCATION.glob("*.mp4"))
|
||||
class TurtleHandler(BaseHandler):
|
||||
def __init__(self):
|
||||
self.entry_string = "Variation of hallo"
|
||||
self.handler = MessageHandler(
|
||||
filters.Regex(r"[hH]([aA]+|[eE]+)[lL]{2,}[oOöÖ]+(le|chen)?") |
|
||||
# react to hello strings
|
||||
filters.Regex(b"\xF0\x9F\x90\xA2".decode("utf8")) |
|
||||
# react to turtle emoji
|
||||
filters.Regex(r"[sS](childkröte)|[tT](urtle)"),
|
||||
# react to turtle string
|
||||
self.entry_point
|
||||
)
|
||||
|
||||
self.handler = MessageHandler(filters.Regex(r"[hH]([aA]+|[eE]+)[lL]{2,}[oOöÖ]+(le)?|(chen)") | # react to hello strings
|
||||
filters.Regex(b"\xF0\x9F\x90\xA2".decode("utf8")) | # react to turtle emoji
|
||||
filters.Regex(r"[sS](childkröte)|[tT](urtle)"), # react to turtle string
|
||||
self.entry_point)
|
||||
pass
|
||||
|
||||
async def entry_point(self, update: Update, context):
|
||||
await super().entry_point(update, context)
|
||||
msgtxt = update.message.text
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
answertxt = "That's a lot of letters!"
|
||||
elif re.search(turtle_emoji, msgtxt):
|
||||
# react to turtle emoji
|
||||
vid=TURTLE_VIDEOS[0]
|
||||
elif re.search(turtle_emoji, msgtxt): # react to turtle emoji
|
||||
vid=TURTLE_VIDEOS[0] # TODO: choose video for smiley reaction
|
||||
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
|
||||
answertxt=turtle_emoji
|
||||
else:
|
||||
vid = random.choice(TURTLE_VIDEOS[1:2]+TURTLE_VIDEOS[3:5]+TURTLE_VIDEOS[6:])
|
||||
answertxt = ""
|
||||
|
||||
if vid != None:
|
||||
if vid!=None:
|
||||
if re.search(turtle_emoji, msgtxt):
|
||||
await update.message.reply_text(text=answertxt)
|
||||
time.sleep(1)
|
||||
|
@ -1,45 +1,22 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from telegram.ext import ExtBot
|
||||
from telegram.error import BadRequest
|
||||
import logging
|
||||
from datetime import time, timedelta, timezone, datetime, date
|
||||
from peewee import fn
|
||||
import models
|
||||
import random
|
||||
|
||||
MEDIA_DIR = Path(os.getenv("MEDIA_DIR"))
|
||||
CHAT_ID = os.getenv("CHAT_ID")
|
||||
|
||||
|
||||
class SetChatPhotoJob():
|
||||
def __init__(self, bot: ExtBot, 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_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 set_random(bot: ExtBot) -> None:
|
||||
"""Set a random chat photo."""
|
||||
if os.getenv("DOCKERIZED", "false") == "false":
|
||||
# only change image on prod
|
||||
return
|
||||
|
||||
photos = list(MEDIA_DIR.glob("*.jpg")) + list(MEDIA_DIR.glob("*.png")) + list(MEDIA_DIR.glob("*.jpeg"))
|
||||
|
||||
if len(photos) == 0:
|
||||
return
|
||||
|
||||
|
||||
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
|
||||
|
||||
chat_id = os.getenv("CHAT_ID")
|
||||
try:
|
||||
await self.bot.set_chat_photo(chat_id, chosen_entry.media_path)
|
||||
except BadRequest:
|
||||
self.logger.error("This is a private chat!")
|
||||
return
|
||||
photo = random.choice(photos)
|
||||
await bot.set_chat_photo(CHAT_ID, photo)
|
||||
|
@ -1,65 +0,0 @@
|
||||
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,13 +5,14 @@ import logging
|
||||
import models
|
||||
from commands import journal, status, turtle, memory, advent
|
||||
from commands.list import list
|
||||
from cronjob import chat_photo, random_memory
|
||||
from cronjob import chat_photo
|
||||
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
level=logging.INFO
|
||||
)
|
||||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||
import asyncio
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -23,16 +24,22 @@ def main() -> None:
|
||||
models.set_db(db_path)
|
||||
application = Application.builder().token(token).build()
|
||||
|
||||
application.add_handler(journal.JournalHandler("journal").handler)
|
||||
application.add_handler(list.ListHandler("list").handler)
|
||||
application.add_handler(status.StatusHandler("status").handler)
|
||||
|
||||
application.add_handler(journal.JournalHandler("journal", models).handler)
|
||||
application.add_handler(list.ListHandler("list", models).handler)
|
||||
application.add_handler(status.StatusHandler("status", models).handler)
|
||||
application.add_handler(turtle.TurtleHandler().handler)
|
||||
application.add_handler(memory.MemoryHandler("memory").handler)
|
||||
application.add_handler(memory.MemoryHandler("memory", models).handler)
|
||||
application.add_handler(advent.AdventsHandler("advent").handler)
|
||||
|
||||
random_memory.RandomMemoryJob(application.bot, application.job_queue)
|
||||
chat_photo.SetChatPhotoJob(application.bot, application.job_queue)
|
||||
# application.add_handler(CommandHandler("help", help_command))
|
||||
# on non command i.e message - echo the message on Telegram
|
||||
# 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
|
||||
application.run_polling()
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
from peewee import *
|
||||
from pathlib import Path
|
||||
import re
|
||||
import os
|
||||
import datetime
|
||||
|
||||
@ -10,14 +9,6 @@ ID_MAPPINGS = {
|
||||
}
|
||||
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.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@ -36,7 +27,7 @@ class JournalEntry(BaseModel):
|
||||
text = TextField(null=True)
|
||||
media_path = TextField(null=True)
|
||||
last_shown = DateField(null=True)
|
||||
rating = IntegerField(null=True) # mapped by RATING_MAPPING
|
||||
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
@ -69,25 +60,6 @@ class JournalEntry(BaseModel):
|
||||
except ValueError: #fck windows
|
||||
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):
|
||||
db.initialize(SqliteDatabase(db_path))
|
||||
|
@ -1,11 +1,12 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
namespace: journal
|
||||
name: journal-bot
|
||||
labels:
|
||||
app: journal-bot
|
||||
spec:
|
||||
# deployment running a single container
|
||||
# deployment running a single container
|
||||
selector:
|
||||
matchLabels:
|
||||
app: journal-bot
|
||||
@ -17,7 +18,8 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: journal
|
||||
image: mollre/journal-bot:1.0.19
|
||||
image: mollre/journal-bot:latest
|
||||
imagePullPolicy: Always
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: journal-secret-env
|
||||
@ -37,9 +39,12 @@ spec:
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
namespace: journal
|
||||
name: "journal-data-nfs"
|
||||
# labels:
|
||||
# directory: "journal-data"
|
||||
spec:
|
||||
storageClassName: ""
|
||||
storageClassName: fast
|
||||
capacity:
|
||||
storage: "5Gi"
|
||||
accessModes:
|
||||
@ -47,17 +52,21 @@ spec:
|
||||
nfs:
|
||||
path: /export/kluster/journal-bot
|
||||
server: 192.168.1.157
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
namespace: journal
|
||||
name: "journal-data-nfs"
|
||||
spec:
|
||||
storageClassName: ""
|
||||
storageClassName: "fast"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: "5Gi"
|
||||
volumeName: journal-data-nfs
|
||||
# selector:
|
||||
# matchLabels:
|
||||
# directory: "journal-data"
|
||||
|
||||
|
@ -1,9 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ./namespace.yaml
|
||||
- ./deployment.yaml
|
||||
- ./sealedsecret.yaml
|
||||
images:
|
||||
- name: mollre/journal-bot
|
||||
newTag: 1.0.68
|
@ -1,6 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: journal
|
||||
labels:
|
||||
name: journal
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"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=="
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": ["minor", "patch"],
|
||||
"matchCurrentVersion": "!/^0/",
|
||||
"automerge": true,
|
||||
"automergeType": "branch",
|
||||
"ignoreTests": true
|
||||
}
|
||||
],
|
||||
"commitMessagePrefix" : "[CI SKIP]"
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user