Initial commit.
This commit is contained in:
commit
a939485495
4 changed files with 170 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
dudlebot.ini
|
||||
dudlebot.pickle
|
152
dudlebot.py
Normal file
152
dudlebot.py
Normal file
|
@ -0,0 +1,152 @@
|
|||
import configparser
|
||||
import logging
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
from enum import Enum
|
||||
from typing import List, Dict
|
||||
|
||||
import telegram
|
||||
from telegram.ext import Updater, CommandHandler, PicklePersistence
|
||||
|
||||
|
||||
class DudleBot:
|
||||
def __init__(self, token):
|
||||
self.logger = logging.getLogger("dudle")
|
||||
|
||||
persistence = PicklePersistence(filename='dudlebot.pickle')
|
||||
|
||||
self.updater = Updater(token, persistence=persistence, use_context=True)
|
||||
dispatcher = self.updater.dispatcher
|
||||
|
||||
dispatcher.add_handler(CommandHandler('start', self.tg_start))
|
||||
dispatcher.add_handler(CommandHandler('newplan', self.tg_newplan))
|
||||
dispatcher.add_handler(CommandHandler('plan', self.tg_plan))
|
||||
|
||||
def tg_start(self, update, _):
|
||||
update.message.reply_text("Hello! May luck be with you and your plans!")
|
||||
|
||||
def tg_newplan(self, update, context):
|
||||
periods = {
|
||||
'thisweek': datetime.today() - timedelta(days=datetime.today().weekday() % 6),
|
||||
'nextweek': datetime.today() + timedelta(days=7) - timedelta(days=datetime.today().weekday() % 6)
|
||||
}
|
||||
|
||||
period = update.message.text.partition(' ')[2]
|
||||
if not period:
|
||||
period = 'nextweek'
|
||||
|
||||
if period not in periods:
|
||||
update.message.reply_text("Sorry man, I just don't understand.")
|
||||
return
|
||||
|
||||
context.chat_data['plan'] = Plan(start=periods[period],
|
||||
duration=timedelta(days=7), # TODO
|
||||
entries={})
|
||||
|
||||
self._reply_with_plan(update, context)
|
||||
|
||||
def tg_plan(self, update, context):
|
||||
if not context.chat_data.get("plan", None):
|
||||
update.message.reply_text("No plan created yet! (Speak /newplan to do so.)")
|
||||
return
|
||||
|
||||
responses_str = update.message.text[len("/plan"):]
|
||||
responses_str = re.sub(r'[^A-Za-z?]', '', responses_str)
|
||||
responses_str = [char.upper() for char in responses_str]
|
||||
|
||||
if len(responses_str) == 0:
|
||||
update.message.reply_text("Did you forget something? (Ex. usage: /plan Y N ? Y N Y Y)")
|
||||
return
|
||||
|
||||
responses_str_padded = [responses_str[i] if i < len(responses_str) else "?"
|
||||
for i in range(context.chat_data['plan'].duration.days)]
|
||||
|
||||
responses = []
|
||||
for char in responses_str_padded:
|
||||
if char == "Y":
|
||||
responses.append(PlanResponse.YES)
|
||||
elif char == "N":
|
||||
responses.append(PlanResponse.NO)
|
||||
else:
|
||||
responses.append(PlanResponse.UNKNOWN)
|
||||
|
||||
user = update.message.from_user
|
||||
name = user.username or f"{user.first_name} {user.last_name}"
|
||||
|
||||
context.chat_data['plan'].entries[user.id] = PlanEntry(name=name, responses=responses)
|
||||
|
||||
self._reply_with_plan(update, context)
|
||||
|
||||
@staticmethod
|
||||
def _reply_with_plan(update, context):
|
||||
plan: Plan = context.chat_data['plan']
|
||||
|
||||
formatted_plan = f"Poll: {plan.start.strftime('%d.%m.%Y')} -> " \
|
||||
f"{(plan.start + plan.duration).strftime('%d.%m.%Y')}"
|
||||
formatted_plan += "\n\n"
|
||||
formatted_plan += "```\n"
|
||||
|
||||
days = [plan.start + timedelta(days=i) for i in range(plan.duration.days)]
|
||||
formatted_plan += f"{'|'.join([day.strftime('%A')[:2] for day in days])}"
|
||||
formatted_plan += "\n"
|
||||
|
||||
if len(plan.entries) == 0:
|
||||
entries = {-1: PlanEntry(name="???", responses=[])}
|
||||
else:
|
||||
entries = plan.entries
|
||||
|
||||
for entry in entries.values():
|
||||
responses = [entry.responses[i] if i < len(entry.responses) else PlanResponse.UNKNOWN
|
||||
for i in range(plan.duration.days)]
|
||||
|
||||
for response in responses:
|
||||
formatted_plan += f"{str(response)} "
|
||||
|
||||
formatted_plan += f" {entry.name}"
|
||||
formatted_plan += "\n"
|
||||
|
||||
formatted_plan += "```"
|
||||
|
||||
update.message.reply_text(formatted_plan, parse_mode=telegram.ParseMode.MARKDOWN)
|
||||
|
||||
def start(self):
|
||||
self.logger.info("Starting DudleBot...")
|
||||
self.updater.start_polling()
|
||||
self.updater.idle()
|
||||
|
||||
|
||||
class PlanResponse(Enum):
|
||||
YES = 1
|
||||
NO = 2
|
||||
UNKNOWN = 3
|
||||
|
||||
def __str__(self):
|
||||
return {
|
||||
self.YES: "Y",
|
||||
self.NO: "N",
|
||||
self.UNKNOWN: "?"
|
||||
}[self]
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlanEntry:
|
||||
name: str
|
||||
responses: List[PlanResponse]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Plan:
|
||||
start: datetime
|
||||
duration: timedelta
|
||||
entries: Dict[int, PlanEntry]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read("dudlebot.ini")
|
||||
|
||||
dudlebot = DudleBot(config.get("general", "token"))
|
||||
dudlebot.start()
|
1
requirements.in
Normal file
1
requirements.in
Normal file
|
@ -0,0 +1 @@
|
|||
python-telegram-bot
|
15
requirements.txt
Normal file
15
requirements.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile
|
||||
#
|
||||
asn1crypto==1.1.0 # via cryptography
|
||||
certifi==2019.9.11 # via python-telegram-bot
|
||||
cffi==1.12.3 # via cryptography
|
||||
cryptography==2.7 # via python-telegram-bot
|
||||
future==0.18.0 # via python-telegram-bot
|
||||
pycparser==2.19 # via cffi
|
||||
python-telegram-bot==12.1.1
|
||||
six==1.12.0 # via cryptography
|
||||
tornado==6.0.3 # via python-telegram-bot
|
Loading…
Reference in a new issue