Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Telegram Bot for notifying new questions #1198

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cms/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ def __init__(self):
# necessary to change it.
# [1] http://freedesktop.org/wiki/Software/shared-mime-info
self.shared_mime_info_prefix = "/usr"
self.telegram_bot_token = None
self.telegram_bot_chat_id = None

# AdminWebServer.
self.admin_listen_address = ""
Expand Down Expand Up @@ -202,6 +204,13 @@ def __init__(self):
# Attempt to load a config file.
self._load(paths)

if bool(self.telegram_bot_token) ^ bool(self.telegram_bot_chat_id):
raise ConfigError("Both telegram_bot_token and telegram_bot_chat_id "
"should be set or left null")
if self.telegram_bot_chat_id:
if type(self.telegram_bot_chat_id) != int:
raise ConfigError("telegram_bot_chat_id should be an integer")
Comment on lines +207 to +212
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checks are probably better moved together with the other Telegram code to improve locality. This should be easier with the below suggestion.


# If the configuration says to print detailed log on stdout,
# change the log configuration.
set_detailed_logs(self.stream_log_detailed)
Expand Down
52 changes: 52 additions & 0 deletions cms/server/contest/communication.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@

import logging

import gevent
import requests

from cms import config
from cms.db import Question, Announcement, Message
from cmscommon.datetime import make_timestamp

Expand Down Expand Up @@ -57,6 +61,49 @@ def __init__(self, subject, text, text_params=None):
self.text_params = text_params


def send_telegram_message(content):
"""Send a message as the Telegram bot with the specified content.
"""
api_url = "https://api.telegram.org/bot{}/sendMessage" \
.format(config.telegram_bot_token)
body = {"chat_id": config.telegram_bot_chat_id,
"text": content,
"parse_mode": "HTML"}
res = requests.post(api_url, data=body)
if res.status_code != 200:
logger.warn("Failed to send Telegram notification: %s", res.json())
else:
logger.debug("Telegram notification sent for the question")


def send_telegram_question_notification(question):
"""Craft and send a Telegram message with the question content.

This is a no-op if the bot is not configured.

This also takes care of formatting the message to be sent, escaping
it and making sure it doesn't exceed the bot message size limit
(4096 bytes).

The message is sent in a different greenlet, so that it doesn't
block the caller if the network is slow.
"""
if not config.telegram_bot_token or not config.telegram_bot_chat_id:
return
trim = lambda s, n: s[:n] + "…" if len(s) > n else s
escape = lambda t: t.replace("<", "&lt;").replace(">", "&gt;")
message = """<b>New question</b>
From: <code>{username}</code>
Subject: <i>{subject}</i>

{text}
""".format(
username=trim(escape(question.participation.user.username), 50),
subject=trim(escape(question.subject), 100),
text=trim(escape(question.text), 3000))
gevent.spawn(send_telegram_message, message)


def accept_question(sql_session, participation, timestamp, subject, text):
"""Add a contestant-submitted question to the database.

Expand Down Expand Up @@ -100,6 +147,11 @@ def accept_question(sql_session, participation, timestamp, subject, text):

logger.info("Question submitted by user %s.", participation.user.username)

try:
send_telegram_question_notification(question)
except Exception as e:
logger.exception("Error occurred while sending Telegram notification")

return question


Expand Down
7 changes: 7 additions & 0 deletions config/cms.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@
"_help": "STL documentation path in the system (exposed in CWS).",
"stl_path": "/usr/share/cppreference/doc/html/",

"_help": "Telegram bot configuration for posting notifications for the",
"_help": "questions. The token is a string obtained from @BotFather and",
"_help": "the chat_id (an int) is the chat id of a conversation where the",
"_help": "bot has access to: a chat where the user started the bot or a",
"_help": "channel where the bot has been authorized.",
"telegram_bot_token": null,
"telegram_bot_chat_id": null,


"_section": "AdminWebServer",
Expand Down