-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from erayerdin/development
v0.1.0b2
- Loading branch information
Showing
13 changed files
with
172 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Limitations | ||
|
||
`tglogger` is not meant to be a replacement for cloud logging services such as Sentry | ||
or Rollbar and there are clear limitations on what you can do with `tglogger`. In this | ||
section, it is aimed to be mention these limitations. | ||
|
||
## Cloud Logging Services | ||
|
||
Cloud logging services provide you | ||
|
||
- the live log captures | ||
- the history of logging | ||
- stats and charts of log records | ||
- email notifications | ||
|
||
and many more. That is why the services you get from these are going to be far superior | ||
than using `tglogger` alone. `tglogger` is meant to be a helper for collaborative and | ||
small-scoped projects and it currently helps you to only; | ||
|
||
- capture the live logging records and | ||
- the history of logging | ||
|
||
with some limitations from both the side of Telegram and this library's way of doing things. | ||
|
||
Also consider that cloud logging services already provide free services with their limitations. | ||
|
||
## Telegram Bot Request Throttle | ||
|
||
[According to Telegram's FAQ on bots](https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this), | ||
Telegram limits the requests made to the server with the amounts below: | ||
|
||
- **30 messages** to *same individual* per **one second or so** | ||
- **1 message** to *different individuals* per **one second or so** | ||
- **20 messages** to *same group* per **one minute** | ||
|
||
The table below can give you idea in what amount you can send logs *theoretically*: | ||
|
||
| | per minute | per hour | per day | per week | per month (30 days) | | ||
|---|---|---|---|---|---| | ||
| **individual** | 1.800 | 108.000 | 2.592.000 | 18.144.000 | 544.320.000 | | ||
| **group** | 20 | 1.200 | 28.800 | 201.600 | 6.048.000 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"ok": true, | ||
"result": { | ||
"message_id": 1, | ||
"from": { | ||
"id": 1, | ||
"is_bot": true, | ||
"username": "foobot" | ||
}, | ||
"date": 1558047713, | ||
"chat": 1 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,86 @@ | ||
import re | ||
from urllib.parse import parse_qs | ||
|
||
import pytest | ||
|
||
import tglogger.request | ||
|
||
|
||
class TestBotSession: | ||
def test_has_token(self, bot_session): | ||
assert hasattr(bot_session, "token") | ||
@pytest.fixture | ||
def send_log_responses( | ||
telegram_handler, log_record_factory, requests_mock, read_test_resource | ||
): | ||
send_message_rule = re.compile( | ||
"https:\/\/api.telegram.org\/bot.+\/sendMessage" | ||
) | ||
send_document_rule = re.compile( | ||
"https:\/\/api.telegram.org\/bot.+\/sendDocument" | ||
) | ||
|
||
requests_mock.register_uri( | ||
"POST", | ||
send_message_rule, | ||
text=read_test_resource("message.response.json").read(), | ||
) | ||
requests_mock.register_uri( | ||
"POST", | ||
send_document_rule, | ||
text=read_test_resource("message.response.json").read(), | ||
) | ||
|
||
log_record = log_record_factory() | ||
return tglogger.request.send_log(telegram_handler, log_record, 1) | ||
|
||
|
||
@pytest.fixture | ||
def generic_info_response(send_log_responses): | ||
return send_log_responses["generic_info_response"] | ||
|
||
|
||
@pytest.fixture | ||
def generic_info_request(generic_info_response): | ||
return generic_info_response.request | ||
|
||
|
||
@pytest.fixture | ||
def generic_info_request_body(generic_info_request): | ||
return parse_qs(generic_info_request.body) | ||
|
||
|
||
class TestSendLogReturn: | ||
def test_isinstance(self, send_log_responses): | ||
assert isinstance(send_log_responses, dict) | ||
|
||
def test_length(self, send_log_responses): | ||
assert len(send_log_responses) == 3 | ||
|
||
def test_has_base_url(self, bot_session): | ||
assert hasattr(bot_session, "base_url") | ||
def test_key_isinstance(self, send_log_responses): | ||
for key in send_log_responses: | ||
assert isinstance(key, str) | ||
|
||
def test_value_isinstance(self, send_log_responses): | ||
for _, value in send_log_responses.items(): | ||
assert ( | ||
isinstance(value, tglogger.request.requests.Response) | ||
or value is None | ||
) | ||
|
||
class TestBotRequest: | ||
def setup_method(self): | ||
self.session = tglogger.request.BotSession("0") | ||
self.request = tglogger.request.BotRequest(self.session, "getMe") | ||
def test_generic_info_response_key_name(self, send_log_responses): | ||
assert "generic_info_response" in send_log_responses | ||
|
||
def test_url(self, bot_session, bot_request_factory): | ||
bot_request = bot_request_factory("getMe") | ||
url = "{base_url}{method_name}".format( | ||
base_url=bot_session.base_url, method_name="getMe" | ||
) | ||
assert bot_request.url == url | ||
def test_stack_trace_response_key_name(self, send_log_responses): | ||
assert "stack_trace_response" in send_log_responses | ||
|
||
def test_django_settings_response_key_name(self, send_log_responses): | ||
assert "django_settings_response" in send_log_responses | ||
|
||
class TestSendMessageRequest: | ||
def test_url(self, bot_send_message_request): | ||
assert bot_send_message_request.url[-11:] == "sendMessage" | ||
|
||
def test_request_body_chat_id( | ||
self, bot_send_message_request, request_body_factory | ||
): | ||
request_body = request_body_factory(bot_send_message_request) | ||
assert request_body.get("chat_id") == 1 | ||
class TestSendLogGenericInfoRequest: | ||
def test_body_chat_id(self, generic_info_request_body): | ||
assert generic_info_request_body["chat_id"][0] == "1" | ||
|
||
def test_request_body_text( | ||
self, bot_send_message_request, request_body_factory | ||
): | ||
request_body = request_body_factory(bot_send_message_request) | ||
assert request_body.get("text") == "foo" | ||
def test_body_parse_mode(self, generic_info_request_body): | ||
assert generic_info_request_body["parse_mode"][0] == "markdown" | ||
|
||
def test_request_body_parse_mode( | ||
self, bot_send_message_request, request_body_factory | ||
): | ||
request_body = request_body_factory(bot_send_message_request) | ||
assert request_body.get("parse_mode") == "Markdown" | ||
def test_body_text(self, generic_info_request_body): | ||
assert "text" in generic_info_request_body |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
__version__ = "0.1.0b1" | ||
__version__ = "0.1.0b2" | ||
__author__ = "Eray Erdin" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,28 @@ | ||
import logging | ||
import typing | ||
|
||
import requests | ||
|
||
_BASE_URL = "https://api.telegram.org/bot{token}/{method}" | ||
|
||
class BotSession(requests.Session): | ||
def __init__(self, token: str): | ||
super().__init__() | ||
self.token = str(token) | ||
self._is_mocked = False | ||
|
||
@property | ||
def base_url(self): | ||
if self._is_mocked: | ||
scheme = "mock" | ||
else: | ||
scheme = "https" | ||
|
||
return "{scheme}://api.telegram.org/bot{token}/".format( | ||
scheme=scheme, token=self.token | ||
) | ||
|
||
|
||
class BotRequest(requests.PreparedRequest): | ||
def __init__(self, session: BotSession, method_name: str): | ||
super().__init__() | ||
self.__session = session | ||
self.__method_name = method_name | ||
self.prepare() | ||
|
||
def prepare_url(self, url, params): | ||
super(BotRequest, self).prepare_url( | ||
"{base_url}{method_name}".format( | ||
base_url=self.__session.base_url, | ||
method_name=self.__method_name, | ||
), | ||
params, | ||
) | ||
|
||
|
||
class SendMessageRequest(BotRequest): | ||
def __init__( | ||
self, | ||
session: BotSession, | ||
chat_id: typing.Union[str, int], | ||
text: str, | ||
parse_mode: str = "Markdown", | ||
disable_web_page_preview: bool = False, | ||
disable_notification: bool = False, | ||
): | ||
try: | ||
chat_id = int(chat_id) | ||
except ValueError: # pragma: no cover | ||
pass # pragma: no cover | ||
|
||
super().__init__(session, "sendMessage") | ||
self.prepare_method("post") | ||
payload = { | ||
def send_log( | ||
handler: logging.Handler, record: logging.LogRecord, chat_id: int | ||
) -> typing.Dict[str, requests.Response]: | ||
""" | ||
Sends log to Telegram chat. | ||
""" | ||
generic_info_response = requests.post( | ||
url=_BASE_URL.format(token=handler.bot_token, method="sendMessage"), | ||
data={ | ||
"chat_id": chat_id, | ||
"text": str(text), | ||
"parse_mode": str(parse_mode), | ||
"disable_web_page_preview": bool(disable_web_page_preview), | ||
"disable_notification": bool(disable_notification), | ||
} | ||
self.prepare_body(data=None, files=None, json=payload) | ||
"text": handler.format(record), | ||
"parse_mode": "markdown", | ||
}, | ||
) | ||
|
||
return { | ||
"generic_info_response": generic_info_response, | ||
"stack_trace_response": None, | ||
"django_settings_response": None, | ||
} |