init commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.venv
|
||||
__pycache__
|
||||
19
LICENCE
Normal file
19
LICENCE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2018 The Python Packaging Authority
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
12
README.md
Normal file
12
README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Logging custom handler for telegram notification
|
||||
## Description
|
||||
Custom logging handler. Used to send CRITICAL level messages to my telegram chat.
|
||||
|
||||
## Installation
|
||||
``pip install git+https://git.lesha.spb.ru/alex/logging_telegram_handler.git``
|
||||
|
||||
## Contacts
|
||||
- **e-mail**: lesha.spb@gmail.com
|
||||
- **telegram**: https://t.me/lesha_spb
|
||||
|
||||
|
||||
31
pyproject.toml
Normal file
31
pyproject.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=75.3.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "LoggingTelegramHandler"
|
||||
description = "Send messages in telegram chat"
|
||||
version = "1.0.0"
|
||||
authors = [
|
||||
{ name = "Aleksei Zosimov", email = "lesha.spb@gmail.com" }
|
||||
]
|
||||
readme = "README.md"
|
||||
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"requests"
|
||||
]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://git.lesha.spb.ru/alex/logging_telegram_handler"
|
||||
Documentation = "https://git.lesha.spb.ru/alex/logging_telegram_handler"
|
||||
Repository = "https://git.lesha.spb.ru/alex/logging_telegram_handler.git"
|
||||
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = [
|
||||
"--import-mode=importlib",
|
||||
]
|
||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
telegram-send @ git+https://bitbucket.org/zosimovaa/telegram_send.git
|
||||
28
src/logging_telegram_handler.egg-info/PKG-INFO
Normal file
28
src/logging_telegram_handler.egg-info/PKG-INFO
Normal file
@@ -0,0 +1,28 @@
|
||||
Metadata-Version: 2.4
|
||||
Name: logging_telegram_handler
|
||||
Version: 0.0.2
|
||||
Summary: Logging telegram handler for important notification.
|
||||
Home-page: https://bitbucket.org/zosimovaa/logging_telegram_handler/src/master/
|
||||
Author: Aleksei Zosimov
|
||||
Author-email: lesha.spb@gmail.com
|
||||
Project-URL: Bug Tracker, https://bitbucket.org/zosimovaa/logging_telegram_handler/src/master/
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Requires-Python: >=3.6
|
||||
Description-Content-Type: text/markdown
|
||||
License-File: LICENCE
|
||||
Dynamic: license-file
|
||||
|
||||
# Logging custom handler for telegram notification
|
||||
## Description
|
||||
Custom logging handler. Used to send CRITICAL level messages to my telegram chat.
|
||||
|
||||
## Installation
|
||||
``pip install git+https://zosimovaa@bitbucket.org/zosimovaa/logging_telegram_handler.git``
|
||||
|
||||
## Contacts
|
||||
- **e-mail**: lesha.spb@gmail.com
|
||||
- **telegram**: https://t.me/lesha_spb
|
||||
|
||||
|
||||
12
src/logging_telegram_handler.egg-info/SOURCES.txt
Normal file
12
src/logging_telegram_handler.egg-info/SOURCES.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
LICENCE
|
||||
README.md
|
||||
pyproject.toml
|
||||
setup.cfg
|
||||
src/logging_telegram_handler/__init__.py
|
||||
src/logging_telegram_handler/handler.py
|
||||
src/logging_telegram_handler/spam_filter.py
|
||||
src/logging_telegram_handler/telegram.py
|
||||
src/logging_telegram_handler.egg-info/PKG-INFO
|
||||
src/logging_telegram_handler.egg-info/SOURCES.txt
|
||||
src/logging_telegram_handler.egg-info/dependency_links.txt
|
||||
src/logging_telegram_handler.egg-info/top_level.txt
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
1
src/logging_telegram_handler.egg-info/top_level.txt
Normal file
1
src/logging_telegram_handler.egg-info/top_level.txt
Normal file
@@ -0,0 +1 @@
|
||||
logging_telegram_handler
|
||||
1
src/logging_telegram_handler/__init__.py
Normal file
1
src/logging_telegram_handler/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .handler import TelegramHandler
|
||||
20
src/logging_telegram_handler/handler.py
Normal file
20
src/logging_telegram_handler/handler.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import os
|
||||
import logging
|
||||
from logging import StreamHandler
|
||||
from .telegram import TelegramSend
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TelegramHandler(StreamHandler):
|
||||
def __init__(self, alias="Test", chat_id='211945135'):
|
||||
StreamHandler.__init__(self)
|
||||
api_key = os.getenv("TELEGRAM_API_KEY")
|
||||
self.telegram = TelegramSend(alias, chat_id=chat_id, api_key=api_key)
|
||||
self.telegram.start()
|
||||
|
||||
def emit(self, msg):
|
||||
self.telegram.send(self.format(msg))
|
||||
|
||||
def stop(self):
|
||||
self.telegram.stop()
|
||||
51
src/logging_telegram_handler/spam_filter.py
Normal file
51
src/logging_telegram_handler/spam_filter.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""
|
||||
The spam filter allows you to limit the distribution of messages in case of
|
||||
a large number of messages in a short period of time.
|
||||
"""
|
||||
|
||||
import time
|
||||
import logging
|
||||
import collections
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SpamFilter:
|
||||
SPAM_MESSAGE = "Spam detected, take a brake :)"
|
||||
|
||||
def __init__(self, threshold=10, control_period=120, silence_period=1800):
|
||||
self.threshold = threshold
|
||||
self.control_period = control_period
|
||||
self.silence_period = silence_period
|
||||
self.spam_buffer = collections.deque(maxlen=self.threshold)
|
||||
self.blocked = False
|
||||
|
||||
def check_block(self):
|
||||
now = time.time()
|
||||
|
||||
# Not initialized yet
|
||||
if len(self.spam_buffer) < self.threshold:
|
||||
self.spam_buffer.append(now)
|
||||
logger.debug("Not a spam. Current len buffer: {}".format(len(self.spam_buffer)))
|
||||
|
||||
# unblocked
|
||||
elif not self.blocked:
|
||||
self.spam_buffer.append(now)
|
||||
timeout = self.spam_buffer[-1] - self.spam_buffer[0]
|
||||
logger.debug("check_block() - not blocked. timeout: {}".format(timeout))
|
||||
if timeout < self.control_period:
|
||||
message = self.SPAM_MESSAGE
|
||||
self.blocked = True
|
||||
logger.warning("Spam detected")
|
||||
|
||||
# blocked
|
||||
else:
|
||||
timeout = now - self.spam_buffer[-1]
|
||||
logger.debug("check_block() - blocked. timeout: {}".format(timeout))
|
||||
if timeout > self.silence_period:
|
||||
self.blocked = False
|
||||
self.spam_buffer = collections.deque(maxlen=self.threshold)
|
||||
self.spam_buffer.append(now)
|
||||
logger.debug("Spam filter unblock")
|
||||
|
||||
return self.blocked
|
||||
72
src/logging_telegram_handler/telegram.py
Normal file
72
src/logging_telegram_handler/telegram.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
The TelegramJustSend class provides sending messages to telegram without guarantees of delivery.
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
import queue
|
||||
import logging
|
||||
import datetime
|
||||
import requests
|
||||
import threading
|
||||
|
||||
from .spam_filter import SpamFilter
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.ERROR)
|
||||
|
||||
|
||||
class TelegramSend(threading.Thread):
|
||||
SILENT_TIME_SINCE = 21
|
||||
SILENT_TIME_BEFORE = 9
|
||||
MAX_BUFFER_SIZE = 20
|
||||
TIMEOUT = 1
|
||||
URL = "https://api.telegram.org/bot{0}/sendMessage?chat_id={1}&text={2}&disable_notification={3}"
|
||||
|
||||
def __init__(self, alias, chat_id, api_key):
|
||||
threading.Thread.__init__(self, daemon=True)
|
||||
self.alias = alias
|
||||
self.chat_id = chat_id
|
||||
self.api_key = api_key
|
||||
self.spam_filter = SpamFilter()
|
||||
self.halt = threading.Event()
|
||||
self.queue = queue.Queue(maxsize=self.MAX_BUFFER_SIZE)
|
||||
|
||||
def __send(self, message):
|
||||
blocked = self.spam_filter.check_block()
|
||||
if not blocked:
|
||||
silent = self.check_silent()
|
||||
message = "[{0}]: {1}".format(self.alias, message)
|
||||
url = self.URL.format(self.api_key, self.chat_id, message, silent)
|
||||
resp = requests.get(url).json()
|
||||
logger.debug(resp)
|
||||
else:
|
||||
self.queue = queue.Queue(maxsize=self.MAX_BUFFER_SIZE)
|
||||
|
||||
def run(self):
|
||||
pid = os.getpid()
|
||||
logger.error(f"Telegram send process started with pid {pid}")
|
||||
while True:
|
||||
try:
|
||||
message = self.queue.get()
|
||||
logger.debug("got message for send: {0}".format(message))
|
||||
self.__send(message)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
finally:
|
||||
time.sleep(self.TIMEOUT)
|
||||
|
||||
def stop(self):
|
||||
self.halt.set()
|
||||
self.join()
|
||||
logger.warning('Stopped')
|
||||
|
||||
def send(self, message):
|
||||
logger.debug("send: {}".format(message))
|
||||
self.queue.put_nowait(message)
|
||||
|
||||
def check_silent(self):
|
||||
now = datetime.datetime.now()
|
||||
silent = True if (now.hour > self.SILENT_TIME_SINCE) or (now.hour < self.SILENT_TIME_BEFORE) else False
|
||||
logger.debug("check_silent(): silent: {0}, hour: {1}".format(silent, now.hour))
|
||||
return silent
|
||||
35
tests/logger_test.py
Normal file
35
tests/logger_test.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import unittest
|
||||
import time
|
||||
|
||||
import logging
|
||||
|
||||
from logging_telegram_handler import TelegramHandler
|
||||
|
||||
|
||||
class MyTestCase(unittest.TestCase):
|
||||
def test_something(self):
|
||||
|
||||
chat_id = "211945135"
|
||||
alias = "[TEST logging_telegram_handler]"
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
telegram_handler = TelegramHandler(chat_id, alias)
|
||||
telegram_handler.setLevel(logging.WARNING)
|
||||
logger.addHandler(telegram_handler)
|
||||
|
||||
logger.critical("critical")
|
||||
logger.error("error")
|
||||
logger.warning("warning")
|
||||
logger.info("info")
|
||||
logger.debug("debug")
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
self.assertEqual(True, True) # add assertion here
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user