ToDo bot

Description

Allows to create TODOs for a small group of users.

Tasks can have on of the following states:

  • TODO – to be done
  • DONE – done
  • CANCELED – nothing was done and not going to be done
  • RELOCATED – task is moved to another task management tool

Warning

Bot keeps only message IDS and task’s description (which is text of the first message). It means that if chat history is deleted, then some task information could be lost. Chat history could be deleted either by user or by telegram itself. What we know about the latter case is that “Bot storage is limited”, though it’s unknow how much or how long messages are kept in telegram servers.

Technical specification

  • /mytasks, /tasks_from_me – shows tasks. By default it shows only WAITING and TODO tasks
    • Prints all tasks in a single message with button “Load more Done”, “Load more WAITING”, “Load more Canceled”
  • /t123 – shows specific task.
    • Prints original forwarded messages
    • You can change status from here
  • /attach123 – attach new messages to a task
  • /stop_attaching – treat next messages as new task
  • /assign123 – assign a task to another person
  • /update_id – current update_id. Can be used to set MIN_UPDATE_ID (see below)
  • /myid – Id and Name of current user

To create new task:

  • Forward message to the bot
  • Assign to a user from the list

Deployment

Create a bot

https://telegram.me/botfather – follow instruction to set bot name and get bot token

Prepare zip file

To make a deployment package execute following commands:

mkdir /tmp/todo-bot
cd /tmp/todo-bot

pip2 install pyTelegramBotAPI -t .
wget https://gitlab.com/itpp/chatops/raw/master/todo-bot/lambda_function.py -O lambda_function.py
zip -r /tmp/todo_bot_package.zip *

Create DynamoDB tables

In AWS (Amazon Web Services): DynamoDB service

Tasks table

It’s used to save tasks

  • Partition key: id (number)
  • Unmark [ ] Use default settings checkbox

Add Secondary index:

  • Partition key: from_id (number)
  • Sort key: task_state (number)
  • Index name: from_id-task_state-index
  • Projected attributes: Include – then add field to_id, description, telegram_unixtime, msg_num

Add another Secondary index:

  • Partition key: to_id (number)
  • Sort key: task_state (number)
  • Index name: to_id-task_state-index
  • Projected attributes: Include – then add field from_id, description, telegram_unixtime, msg_num, next_reminder

Users table

It’s used to save current user activity. For example, if user sends batch of forwarded message, we need to change user status to save all messages to a single task.

  • Partition key: user_id (number)

Create Lambda function

Runtime

In AWS: Lambda service

Use Python 2.7

Environment variables

In AWS: Lambda service

  • BOT_TOKEN – the one you got from BotFather

  • USERS – Dictionary of users who can be assigned to a task. Format: {"USER_ID": "USER_NAME"}. At this moment there is no API to get list of members. As a workaround you can ask users to send /myid command to get name and id and prepare the dictionary manually. To use emoji in user names to as following:

  • DYNAMODB_TABLE_TASK – table with tasks (name of the table)

  • DYNAMODB_TABLE_USER – table with users (name of the table)

  • LOG_LEVELDEBUG or INFO

  • MIN_UPDATE_ID – Number to distract from update_id in task’s id computation. Use /update_id to get value.

  • FORWARDING_DELAY – max seconds to wait for next forwarded message. It’s a workaround for limitation of telegram API – it sends forwarded messages one by one and never in a single event. Default is 3 sec.

  • REMINDER_DAYS – how much days to wait before remind a user about open task

Trigger

In AWS: Lambda service

  • API Gateway. Once you configure it and save, you will see Invoke URL under Api Gateway details section
  • CloudWatch Events. Create new rule for reminders, for example set
    • Rule nameboto-todo-reminder
    • Schedule expressionrate(1 day)

Role

In AWS: IAM (Identity and Access Management) service: Policies

  • Create policy of actions for DynamoDB:
    • ServiceDynamoDB
    • ActionAll DynamoDB actions
    • ResourcesAll Resources

In AWS: IAM service: Roles

In list of roles choose the role, which was named in process of creating lambda function, and attach to it recently created policy for DynamoDB

  • The role must allow access to lambda and dynamodb services.

By the final, role should look something like this:

In AWS: Lambda service: Designer: View Permissions (Key-Icon)

{

     "roleName": "{ROLE_NAME}",
      "policies": [
        {
          "document": {
            "Version": "2012-10-17",
            "Statement": [
              {
                "Effect": "Allow",
                "Action": [
                  "logs:CreateLogGroup",
                  "logs:CreateLogStream",
                  "logs:PutLogEvents"
                ],
                "Resource": [
                  "arn:aws:logs:*:*:*"
                ]
              }
            ]
          },
          "name": "AWSLambdaEdgeExecutionRole-daf8b371-4fc9-4e1a-9809-fcd44b96d4f2",
          "id": "ANPAX7765LQXBC72HXN4W",
          "type": "managed",
          "arn": "arn:aws:iam::549753543726:policy/service-role/AWSLambdaEdgeExecutionRole-daf8b371-4fc9-4e1a-9809-fcd44b96d4f2"
          },
        {
          "document": {
            "Version": "2012-10-17",
            "Statement": [
              {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": "dynamodb:*",
                "Resource": "*"
              }
            ]
          },
          "name": "{NAME_OF_POLICY_FOR_DYNAMODB}",
          "id": "ANPAX7765LQXJUGC2FXMV",
          "type": "managed",
          "arn": "arn:aws:iam::549753543726:policy/{NAME_OF_POLICY_FOR_DYNAMODB}"
        }
      ],
      "trustedEntities": [
        "edgelambda.amazonaws.com",
        "lambda.amazonaws.com"
      ]

}

Timeout

in AWS: Lambda service

Execution time depends on telegram server and amount of requests there. So, think about 30 seconds for limit.

Concurrency

in AWS: Lambda service

You may need to disable concurrency (i.e. set Reserve concurrency to value 1) as a workaround for following issue: on resending batch of messages, those might be processed by several workers, so you might get several messages instead of a single one.

Register webhook at telegram

via python lib

Execute once in python console:

BOT_TOKEN = "PASTETHETOKEN"
WEB_HOOK = "PASTEAWSWEBHOOK"

import telebot  # https://github.com/eternnoir/pyTelegramBotAPI
bot = telebot.TeleBot(BOT_TOKEN, threaded=False)
bot.set_webhook(WEB_HOOK)

via curl

# TODO pass allowed_updates arg
curl -XPOST https://api.telegram.org/bot<YOURTOKEN>/setWebhook\?url\=YOURAPIGATEWAYURL

via url

Type the following in your browser and hit enter. (Make sure to substitute the place holder text):

https://api.telegram.org/bot<your-bot-token>/setWebHook?url=<your-API-invoke-URL>