Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Telegram resending bot on AWS Lambda. OBSOLETE: the latest version is here

The general idea is to ask a group (let's call it target group) via bot and get answer. The group might be:

  • Support Team
  • IT Department of your company
  • etc.

TODO: text formatting is lost on resending. Use entities instead of text field

TODO: set debug level via Environment variables

Technical specification

  • /thischat -- returns id of current chat. It's used to get chat ID of the group

  • The ways to send message-reqest to the bot:

    • In private chat with the bot: any message

    • In a group (it's another, not the target group):

      • Any message that starts with / and ends with @<name_of_the_>bot, e.g. /hey@super_bot, /please@request_bot, etc.
      • as a reply to bot's message
  • On getting message-request

    • The target group receives a copy with a reference to the sender and original message
  • Once someone replies bot's message a copy of the response is sent back to chat of original message

    • Response from target group is anonymous by default, but could be customized


  • In original chat from Ivan /hey Answer to the Ultimate Question of Life, the Universe, and Everything?
  • In target group from @name_of_the_bot: <a href="userlink">Ivan</a>: Answer to the Ultimate Question of Life, the Universe, and Everything? *msg:<message1>:<chat>*
  • In target group from @Deep_thought1:Anybody knows the answer?
  • In target group from @Deep_thought2:Let me think a little bit?
  • In target group from @Deep_thought1:?
  • In target group from @Deep_thought2:Ok, I found and checked the answer!
  • In target group from @Deep_thought2: In reply to Ivan: Answer to the Ultimate Question ... The answer is 42!
  • In original chat from @name_of_the_bot: The answer is 42! *msg:<message2>*

Harambify bot was used as an example to create this bot.

Create a bot -- follow instruction to set bot name and get bot token

Prepare zip file

To make a deployment package execute following commands::

cd /path/to/new/folder

pip2 install pyTelegramBotAPI -t .
wget -O
zip -r /tmp/ *

Create Lambda function


Use Python 2.7

Environment variables

  • BOT_TOKEN -- the one you got from BotFather
  • TARGET_GROUP -- skip if you don't know it. Send command to the bot /thischat from the target group. Then set this variable
  • ANONYMOUS_REPLY -- whether to send replies anonymously. Defaul True
  • ANONYMOUS_REQUEST_FROM_GROUPS -- whether to show author name on requesting from another group. Defaul True


User API Gateway. Once you configure it and save, you will see Invoke URL under Atpi Gateway details section

Register webhook at telegram

via python lib

Execute once in python console::


import telebot  #
bot = telebot.TeleBot(BOT_TOKEN, threaded=False)
bot.set_webhook(WEB_HOOK, allowed_updates=['message'])

via curl

# TODO pass allowed_updates arg
import telebot #
import sys
import os
import logging
import re
logger = logging.getLogger()
# logger.setLevel(logging.DEBUG)
RESPONSE_200 = {
"statusCode": 200,
"headers": { },
"body": ""
MEDIA = {'sticker': 'send_sticker', 'voice': 'send_voice', 'video': 'send_video', 'document': 'send_document', 'video_note': 'send_video_note'}
def lambda_handler(event, context):
logger.debug("Event: \n%s", event)
logger.debug("Context: \n%s", context)
# READ webhook data
# Object Update in json format.
# See
update = telebot.types.JsonDeserializable.check_json(event["body"])
# READ environment variables
BOT_TOKEN = os.environ['BOT_TOKEN']
TARGET_GROUP = int(os.environ.get('TARGET_GROUP', 0))
ANONYMOUS_REPLY = os.environ.get('ANONYMOUS_REPLY') != 'False'
message = update.get('message')
if not message:
return RESPONSE_200
chat = message.get('chat')
user = message.get('from')
# Only work with disabled threaded mode. See
bot = telebot.TeleBot(BOT_TOKEN, threaded=False)
def get_command_and_text(text):
"""split message into command and main text"""
m = re.match('(/[^ @]*)([^ ]*)(.*)', text, re.DOTALL)
if m:
# group(3) is a bot name
return None, text
command, main_text = get_command_and_text(message.get('text', ''))
if command and command == '/thischat':
bot.send_message(chat['id'], chat['id'], reply_to_message_id=message['message_id'])
return RESPONSE_200
original_chat = None
original_message_id = None
if not command and message.get('reply_to_message'):
# try to get the message reference via reply_to_message
parent_text = message['reply_to_message'].get('text', '')
m ='msg:([0-9-]*):?([0-9-]*)$', parent_text)
if m:
original_message_id =
original_chat =
if not main_text and not any(message.get(key) for key in MEDIA) and not message.get('photo'):
bot.send_message(chat['id'], "<i>Empty message is ignored</i>", reply_to_message_id=message['message_id'], parse_mode='HTML')
return RESPONSE_200
is_from_target_group = chat['id'] == TARGET_GROUP
if is_from_target_group and not original_chat:
bot.send_message(chat['id'], "<i>At this chat you can only reply to the requests</i>", reply_to_message_id=message['message_id'], parse_mode='HTML')
return RESPONSE_200
reply_chat = original_chat or TARGET_GROUP
from_group = not is_from_target_group and chat.get('title')
show_username = not (is_from_target_group and ANONYMOUS_REPLY or from_group and ANONYMOUS_REQUEST_FROM_GROUPS)
if from_group:
from_group = '<em>from</em> <b>%s</b>' % from_group
reply_text = "%s%s\n%s\n<i>msg:%s%s</i>" % (
from_group or '',
': ' if main_text and (from_group or show_username) else '',
':%s' % chat['id'] if not is_from_target_group else '')
if show_username:
author_link = "<a href=\"tg://user?id=%s\">%s</a>" % (user['id'], user['first_name'])
reply_text = "%s%s%s" % (
' ' if from_group else '',
for key, method in MEDIA.items():
if message.get(key):
# media are sent separately
getattr(bot, method)(reply_chat, message[key]['file_id'], reply_to_message_id=original_message_id or None)
if message.get('photo'):
photo = message.get('photo')[-1]
bot.send_photo(reply_chat, photo['file_id'], caption=message.get('caption'))
bot.send_message(reply_chat, reply_text, reply_to_message_id=original_message_id or None, parse_mode='HTML')
return RESPONSE_200
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment