1
0

9 Commits

5 changed files with 88 additions and 99 deletions

View File

@@ -3,4 +3,4 @@ WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY src .
CMD [ "python", "bot.py"]
CMD [ "python", "-u", "bot.py"]

View File

@@ -1,3 +1,5 @@
# this is the old version of the bot!!
# todo.txt as a Telegram bot
A bot to hold your todo.txt tasks
@@ -9,8 +11,7 @@ A bot to hold your todo.txt tasks
3. create `YOUR_CONFIG_DIRECTORY/config.json`:
```json
{
"token": "285908433:AAHyxGFXtG37Ox_AvPPYB_J73iNpT7rk1JM"
, "tasks_file": "./tasks.json"
"token": "YOUR_BOT_TOKEN"
}
```
4. run `cp docker-compose.example docker-compose.yml`

30
help.md
View File

@@ -1,30 +0,0 @@
# commands
Anything sent without a command is assumed to be a new task to be added
## actions on tasks
- `/add <task-text>` - Add a new task
- `/do <id> [id [id [id]...]]` - Do task(s)
- `/priority <priority> <id> [id [id [id]...]]` - Set the priority of task(s)
- `/rm <id> [id [id [id]...]]` - Remove task(s)
- `/undo <id> [id [id [id]...]]` - undo task(s)
### fuzzy actions
- `/fdo <text to match>`
- `/fpriority <text to match>`
- `/frm <text to match>`
- `/fundo <text to match>`
## general
- `/export` - Send all tasks as plaintext
- `/help` - Show help information
- `/ls [filters]` - List tasks
- `/last` - Run the last command sent
- `/marco` - Test if bot is up
## /ls filters
- `f[ilter]:<text>` - Tasks must have this text in it
- `!f[ilter]:<text>` - Tasks must **not** have this text in it
- `:show-done` - Show and include done tasks
- `:only-done` - Show only done tasks

View File

@@ -14,7 +14,7 @@ from fuzzywuzzy import process
from fuzzywuzzy import fuzz
from pydo import Task
VERSION = "v1.1"
VERSION = "v1.2.1"
PROPERTY_LAST_COMMAND = "last_command"
@@ -22,7 +22,7 @@ PROPERTY_LAST_ARGUMENTS = "last_arguments"
CONFIG_FILE = '/config/config.json'
ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
TASKS_FILE="/config/tasks.json"
TASKS_FILE = "/config/tasks.json"
with open(CONFIG_FILE) as file:
@@ -37,7 +37,6 @@ def on_message(msg):
:param msg: The message object, passed by MessageLoop
"""
content_type, chat_type, chat_id = telepot.glance(msg)
print(content_type, chat_type, chat_id)
if content_type != 'text':
BOT.sendMessage(chat_id, "I can only understand text, sorry.")
@@ -49,16 +48,16 @@ def on_message(msg):
if command == '/last':
last_checks(chat_id)
command = get_property(PROPERTY_LAST_COMMAND, chat_id)
arguments = get_property(PROPERTY_LAST_ARGUMENTS, chat_id)
command = get_property(chat_id, PROPERTY_LAST_COMMAND)
arguments = get_property(chat_id, PROPERTY_LAST_ARGUMENTS)
else:
set_property(PROPERTY_LAST_COMMAND, command, chat_id)
set_property(PROPERTY_LAST_ARGUMENTS, arguments, chat_id)
set_property(chat_id, PROPERTY_LAST_COMMAND, command)
set_property(chat_id, PROPERTY_LAST_ARGUMENTS, arguments)
process_command(command, arguments, chat_id)
process_command(chat_id, command, arguments)
def process_command(command, arguments, chat_id):
def process_command(chat_id, command, arguments):
"""
Processes the command sent by user
:param command: The command itself i.e /add
@@ -70,15 +69,17 @@ def process_command(command, arguments, chat_id):
elif command == '/help':
user_help_info(chat_id)
elif command == '/add':
add_task(Task(" ".join(arguments)), chat_id)
add_task(chat_id, Task(" ".join(arguments)))
elif command == '/rm':
rm_tasks(arguments, chat_id)
function_fuzzy_checker(chat_id, arguments, rm_tasks)
elif command == '/ls':
ls_tasks(arguments, chat_id)
ls_tasks(chat_id, arguments)
elif command == '/do':
do_tasks(arguments, chat_id)
# do_tasks(chat_id, arguments)
function_fuzzy_checker(chat_id, arguments, do_tasks)
elif command == '/undo':
undo_tasks(arguments, chat_id)
# undo_tasks(chat_id, arguments)
function_fuzzy_checker(chat_id, arguments, undo_tasks)
elif command == '/export':
export_tasks(chat_id)
elif command == '/marco':
@@ -86,25 +87,19 @@ def process_command(command, arguments, chat_id):
elif command == '/delete_all_tasks':
delete_all_tasks(chat_id)
elif command == '/priority':
priority(chat_id, arguments)
elif command == '/fdo':
fuzzy_action(chat_id, ' '.join(arguments), do_tasks)
elif command == '/fundo':
fuzzy_action(chat_id, ' '.join(arguments), undo_tasks)
elif command == '/frm':
fuzzy_action(chat_id, ' '.join(arguments), rm_tasks)
elif command == '/fpriority':
fuzzy_priority(chat_id, arguments)
#priority(chat_id, arguments)
function_fuzzy_checker(chat_id, arguments, priority, ignore=[0],
handler=fuzzy_priority_handler)
else:
set_property(PROPERTY_LAST_COMMAND, '/add', chat_id)
set_property(PROPERTY_LAST_ARGUMENTS, arguments, chat_id)
set_property(chat_id, PROPERTY_LAST_COMMAND, '/add')
set_property(chat_id, PROPERTY_LAST_ARGUMENTS, arguments)
# command has to prefixed here since there is no actual command with a
# preceding slash
add_task(Task(command + " " + " ".join(arguments)), chat_id)
add_task(chat_id, Task(command + " " + " ".join(arguments)))
def add_task(task, chat_id):
def add_task(chat_id, task):
"""
Adds a task
:param task: A Task object
@@ -112,11 +107,11 @@ def add_task(task, chat_id):
"""
tasks = get_tasks(chat_id)
tasks.append(task)
set_tasks(tasks, chat_id)
set_tasks(chat_id, tasks)
BOT.sendMessage(chat_id, "Added task: {0}".format(task))
def rm_tasks(task_ids, chat_id):
def rm_tasks(chat_id, task_ids):
"""
Delete multiple tasks
:param task_ids: An iterable of IDs of task objects
@@ -126,11 +121,11 @@ def rm_tasks(task_ids, chat_id):
for i in task_ids:
if not is_task_id_valid(chat_id, i):
continue
set_tasks([x for x in tasks if str(tasks[int(i)]) != str(x)], chat_id)
set_tasks(chat_id, [x for x in tasks if str(tasks[int(i)]) != str(x)])
BOT.sendMessage(chat_id, "Removed task: {0}".format(tasks[int(i)]))
def get_property(property_name, chat_id):
def get_property(chat_id, property_name):
"""
// TODO figure out what this does
:param property_name:
@@ -147,7 +142,7 @@ def get_property(property_name, chat_id):
return None
def set_property(property_name, value, chat_id):
def set_property(chat_id, property_name, value):
"""
// TODO figure out what this does
:param property_name:
@@ -198,7 +193,7 @@ def get_tasks(chat_id, raw=False):
return tasks
def get_task(task_id, chat_id):
def get_task(chat_id, task_id):
"""
Returns single task
:param task_id: ID of task
@@ -210,7 +205,7 @@ def get_task(task_id, chat_id):
return get_tasks(chat_id)[int(task_id)]
def set_tasks(tasks, chat_id):
def set_tasks(chat_id, tasks):
"""
Overwrite the existing tasks with a new list
:param tasks: Iterable of Task objects
@@ -229,7 +224,7 @@ def set_tasks(tasks, chat_id):
tasks_file.write(json.dumps(task_dict))
def set_task(task_id, task, chat_id):
def set_task(chat_id, task_id, task):
"""
Overwrite a single task by ID
:param task_id: ID of the task
@@ -240,10 +235,10 @@ def set_task(task_id, task, chat_id):
return
tasks = get_tasks(chat_id)
tasks[task_id] = task
set_tasks(tasks, chat_id)
set_tasks(chat_id, tasks)
def ls_tasks(arguments, chat_id):
def ls_tasks(chat_id, arguments):
"""
Send a list of tasks to user
:param arguments: Iterable of strings
@@ -310,7 +305,7 @@ def ls_tasks(arguments, chat_id):
BOT.sendMessage(chat_id, text)
def do_tasks(task_ids, chat_id):
def do_tasks(chat_id, task_ids):
"""
Mark tasks by ID as done
:param task_ids: Iterable of task IDs
@@ -320,13 +315,13 @@ def do_tasks(task_ids, chat_id):
if not is_task_id_valid(chat_id, i):
continue
task = get_task(int(i), chat_id)
task = get_task(chat_id, int(i))
task.do()
set_task(int(i), task, chat_id)
set_task(chat_id, int(i), task)
BOT.sendMessage(chat_id, "Did task {1}: {0}".format(str(task), i))
def undo_tasks(task_ids, chat_id):
def undo_tasks(chat_id, task_ids):
"""
Mark tasks as not done
:param task_ids: Iterable of task IDs
@@ -335,9 +330,9 @@ def undo_tasks(task_ids, chat_id):
for i in task_ids:
if not is_task_id_valid(chat_id, i):
continue
task = get_task(int(i), chat_id)
task = get_task(chat_id, int(i))
task.undo()
set_task(int(i), task, chat_id)
set_task(chat_id, int(i), task)
BOT.sendMessage(chat_id, "Undid task {1}: {0}".format(str(task), i))
@@ -368,8 +363,8 @@ def last_checks(chat_id):
Checks if the user has sent a command already
:param chat_id: Telegram chat_id
"""
if get_property(PROPERTY_LAST_ARGUMENTS, chat_id) is None or \
get_property(PROPERTY_LAST_COMMAND, chat_id) is None:
if get_property(chat_id, PROPERTY_LAST_ARGUMENTS) is None or \
get_property(chat_id, PROPERTY_LAST_COMMAND) is None:
BOT.sendMessage(chat_id, "No recorded last command")
def user_init(chat_id):
@@ -388,7 +383,7 @@ def user_help_info(chat_id):
with open('help.md') as help_file:
text = help_file.read()
text += "\ntodo.txt bot for Telegram version {0}".format(VERSION)
text += "\n[View help on GitHub](alvierahman90.github.io/todo.txt_telegram/help.html)"
text += "\n[View help on GitHub](alvierahman90.github.io/todo.txt_telegram/src/help.html)"
BOT.sendMessage(chat_id, text, parse_mode='Markdown')
@@ -399,7 +394,7 @@ def delete_all_tasks(chat_id):
:param chat_id: Telegram chat id
"""
export_tasks(chat_id)
set_tasks([], chat_id)
set_tasks(chat_id, [])
BOT.sendMessage(chat_id, "Deleted all tasks.")
@@ -407,6 +402,10 @@ def priority(chat_id, arguments):
"""
Changes the priority of a task
"""
BOT.sendMessage(chat_id, repr(arguments))
for i, argument in enumerate(arguments):
BOT.sendMessage(chat_id, f"Argument {i}: {argument}")
if len(arguments) < 2:
BOT.sendMessage(chat_id, "Not enough arguments: /priority PRIORITY"
"ID-OF-TASK [ID-OF-TASK...]")
@@ -436,7 +435,7 @@ def priority(chat_id, arguments):
BOT.sendMessage(chat_id, "Setting priority of '{}'.".format(tasks[i]))
tasks[i].priority = new_priority
set_tasks(tasks, chat_id)
set_tasks(chat_id, tasks)
return
@@ -466,8 +465,6 @@ def is_task_id_valid(chat_id, task_id):
else:
return fail()
print(real_task_id)
print(len(get_tasks(chat_id)))
if real_task_id < len(get_tasks(chat_id)):
return real_task_id
return fail()
@@ -494,19 +491,46 @@ def fuzzy_action(chat_id, text, function):
:param function: the function with which to process the task_id
"""
task_id, matchness = fuzzy_get_task_id_from_text(chat_id, text)
return function([task_id], chat_id)
return function(chat_id, [task_id])
def fuzzy_priority(chat_id, arguments):
def fuzzy_priority_handler(chat_id, text, function):
"""
Sets the priority of the closest matching task to text
:param chat_id: Telegram chat_id
:param text: text to match to a task to perform function on it
:param function: the function with which to process the task_id
:param function: the function with which to process the task_id (not used)
"""
text = ' '.join(arguments[1:])
task_id, matchness = fuzzy_get_task_id_from_text(chat_id, text)
return priority(chat_id, [arguments[0], task_id])
# TODO allow fuzzy_action to run on commands with more positional arguments
arguments = text.split(' ')
new_priority = arguments.pop(0)
task_id, matchness = fuzzy_get_task_id_from_text(chat_id, ' '.join(arguments))
BOT.sendMessage(chat_id, "fprepr: " + repr([new_priority, task_id]))
return priority(chat_id, [new_priority, task_id])
def function_fuzzy_checker(chat_id, arguments, function, ignore=[], handler=fuzzy_action):
"""
Checks if the all arguments are numerical and if they are it runs the
function, otherwise it runs fuzzy_action with that function.
:param chat_id: Telegram chat_id
:param arguments: list of arguments form process_command
:param function: the function to be executed
:param ignore: a list of argument indexes to ignore when checking if to run fuzzy
:param handler: the function which executes the function in fuzzy mode
"""
numerical = True
for i, val in enumerate(arguments):
if i in ignore:
continue
if not val.isdigit():
numerical = False
break
if not numerical:
handler(chat_id, ' '.join(arguments), function)
else:
function(chat_id, arguments)
if __name__ == "__main__":

View File

@@ -4,16 +4,10 @@ Anything sent without a command is assumed to be a new task to be added
## actions on tasks
- `/add <task-text>` - Add a new task
- `/do <id> [id [id [id]...]]` - Do task(s)
- `/priority <priority> <id> [id [id [id]...]]` - Set the priority of task(s)
- `/rm <id> [id [id [id]...]]` - Remove task(s)
- `/undo <id> [id [id [id]...]]` - undo task(s)
### fuzzy actions
- `/fdo <text to match>`
- `/fpriority <text to match>`
- `/frm <text to match>`
- `/fundo <text to match>`
- `/do` - Mark a task as done
- `/priority` - Set the priority of a task
- `/rm` - Remove a task
- `/undo` - Mark a task as not done
## general
- `/export` - Send all tasks as plaintext