cli2telegram 2.0.0__tar.gz → 2.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cli2telegram-2.0.0 → cli2telegram-2.0.2}/PKG-INFO +18 -13
- {cli2telegram-2.0.0 → cli2telegram-2.0.2}/cli2telegram/cli.py +19 -11
- {cli2telegram-2.0.0 → cli2telegram-2.0.2}/cli2telegram/daemon.py +27 -20
- {cli2telegram-2.0.0 → cli2telegram-2.0.2}/cli2telegram/util.py +9 -10
- cli2telegram-2.0.2/pyproject.toml +51 -0
- cli2telegram-2.0.0/cli2telegram.egg-info/PKG-INFO +0 -170
- cli2telegram-2.0.0/cli2telegram.egg-info/SOURCES.txt +0 -17
- cli2telegram-2.0.0/cli2telegram.egg-info/dependency_links.txt +0 -1
- cli2telegram-2.0.0/cli2telegram.egg-info/entry_points.txt +0 -3
- cli2telegram-2.0.0/cli2telegram.egg-info/requires.txt +0 -4
- cli2telegram-2.0.0/cli2telegram.egg-info/top_level.txt +0 -2
- cli2telegram-2.0.0/setup.cfg +0 -4
- cli2telegram-2.0.0/setup.py +0 -89
- cli2telegram-2.0.0/tests/__init__.py +0 -22
- cli2telegram-2.0.0/tests/test_message_processing.py +0 -97
- {cli2telegram-2.0.0 → cli2telegram-2.0.2}/LICENSE +0 -0
- {cli2telegram-2.0.0 → cli2telegram-2.0.2}/README.md +0 -0
- {cli2telegram-2.0.0 → cli2telegram-2.0.2}/cli2telegram/__init__.py +0 -0
- {cli2telegram-2.0.0 → cli2telegram-2.0.2}/cli2telegram/config.py +0 -0
- {cli2telegram-2.0.0 → cli2telegram-2.0.2}/cli2telegram/const.py +0 -0
|
@@ -1,23 +1,29 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: cli2telegram
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: Small utility to send Telegram messages from the CLI.
|
|
5
|
-
|
|
5
|
+
License: AGPL-3.0-or-later
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: cli,telegram,notification
|
|
6
8
|
Author: Markus Ressel
|
|
7
9
|
Author-email: mail@markusressel.de
|
|
8
|
-
|
|
9
|
-
Platform: UNKNOWN
|
|
10
|
+
Requires-Python: >=3.12,<4.0
|
|
10
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
12
|
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
|
12
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.
|
|
18
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Requires-Dist: aiofiles
|
|
20
|
+
Requires-Dist: click
|
|
21
|
+
Requires-Dist: container-app-conf
|
|
22
|
+
Requires-Dist: emoji
|
|
23
|
+
Requires-Dist: python-telegram-bot
|
|
24
|
+
Project-URL: Homepage, https://github.com/markusressel/cli2telegram
|
|
25
|
+
Project-URL: Repository, https://github.com/markusressel/cli2telegram
|
|
19
26
|
Description-Content-Type: text/markdown
|
|
20
|
-
License-File: LICENSE
|
|
21
27
|
|
|
22
28
|
# cli2telegram
|
|
23
29
|
Small utility to send Telegram messages from the CLI.
|
|
@@ -167,4 +173,3 @@ You should have received a copy of the GNU Affero General Public License
|
|
|
167
173
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
168
174
|
```
|
|
169
175
|
|
|
170
|
-
|
|
@@ -13,17 +13,19 @@
|
|
|
13
13
|
#
|
|
14
14
|
# You should have received a copy of the GNU Affero General Public License
|
|
15
15
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
-
|
|
16
|
+
import asyncio
|
|
17
17
|
import logging
|
|
18
18
|
import sys
|
|
19
19
|
from typing import Tuple, List
|
|
20
20
|
|
|
21
21
|
import click
|
|
22
|
+
from telegram.ext import Application
|
|
22
23
|
|
|
24
|
+
from cli2telegram import util
|
|
23
25
|
from cli2telegram.config import Config
|
|
24
26
|
from cli2telegram.const import CODEBLOCK_MARKER_END, CODEBLOCK_MARKER_START, TELEGRAM_MESSAGE_LENGTH_LIMIT
|
|
25
27
|
from cli2telegram.daemon import Daemon
|
|
26
|
-
from cli2telegram.util import split_message, prepare_code_message
|
|
28
|
+
from cli2telegram.util import split_message, prepare_code_message
|
|
27
29
|
|
|
28
30
|
LOGGER = logging.getLogger(__name__)
|
|
29
31
|
# LOGGER.setLevel(logging.DEBUG)
|
|
@@ -86,15 +88,9 @@ def cli(bot_token: str or None, chat_id: str or None, code_block: bool, lines: T
|
|
|
86
88
|
text = "".join(lines)
|
|
87
89
|
messages = prepare_messages(text, code_block)
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
chat_id=CONFIG.TELEGRAM_CHAT_ID.value,
|
|
93
|
-
message=message,
|
|
94
|
-
retry=CONFIG.RETRY_ENABLED.value,
|
|
95
|
-
retry_timeout=CONFIG.RETRY_TIMEOUT.value,
|
|
96
|
-
give_up_after=CONFIG.RETRY_GIVE_UP_AFTER.value
|
|
97
|
-
)
|
|
91
|
+
app = Application.builder().token(CONFIG.TELEGRAM_BOT_TOKEN.value).build()
|
|
92
|
+
|
|
93
|
+
asyncio.run(_send_messages(app, messages))
|
|
98
94
|
|
|
99
95
|
|
|
100
96
|
def prepare_messages(text: str, code_block: bool) -> List[str]:
|
|
@@ -114,5 +110,17 @@ def prepare_messages(text: str, code_block: bool) -> List[str]:
|
|
|
114
110
|
return result
|
|
115
111
|
|
|
116
112
|
|
|
113
|
+
async def _send_messages(app, messages):
|
|
114
|
+
for message in messages:
|
|
115
|
+
await util.try_send_message(
|
|
116
|
+
app=app,
|
|
117
|
+
chat_id=CONFIG.TELEGRAM_CHAT_ID.value,
|
|
118
|
+
message=message,
|
|
119
|
+
retry=CONFIG.RETRY_ENABLED.value,
|
|
120
|
+
retry_timeout=CONFIG.RETRY_TIMEOUT.value,
|
|
121
|
+
give_up_after=CONFIG.RETRY_GIVE_UP_AFTER.value
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
117
125
|
if __name__ == '__main__':
|
|
118
126
|
cli()
|
|
@@ -13,20 +13,19 @@
|
|
|
13
13
|
#
|
|
14
14
|
# You should have received a copy of the GNU Affero General Public License
|
|
15
15
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
-
|
|
16
|
+
import asyncio
|
|
17
17
|
import logging
|
|
18
18
|
import os
|
|
19
|
-
import queue
|
|
20
19
|
import sys
|
|
21
|
-
import threading
|
|
22
20
|
from signal import signal, SIGINT, SIGTERM
|
|
23
21
|
from time import time, sleep
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
import aiofiles
|
|
24
|
+
from telegram.ext import ApplicationBuilder
|
|
26
25
|
|
|
27
26
|
from cli2telegram import RetryLimitReachedException
|
|
28
27
|
from cli2telegram.config import Config
|
|
29
|
-
from cli2telegram.util import
|
|
28
|
+
from cli2telegram.util import try_send_message, prepare_code_message
|
|
30
29
|
|
|
31
30
|
LOGGER = logging.getLogger(__name__)
|
|
32
31
|
HANDLER = logging.StreamHandler(sys.stdout)
|
|
@@ -40,8 +39,8 @@ class Daemon:
|
|
|
40
39
|
self.config = config
|
|
41
40
|
self.running = True
|
|
42
41
|
self.pipe_file_path = config.DAEMON_PIPE_PATH.value
|
|
43
|
-
self.message_queue =
|
|
44
|
-
self.
|
|
42
|
+
self.message_queue = asyncio.Queue()
|
|
43
|
+
self._app = ApplicationBuilder().token(config.TELEGRAM_BOT_TOKEN.value).build()
|
|
45
44
|
|
|
46
45
|
def run(self):
|
|
47
46
|
if os.path.exists(self.pipe_file_path):
|
|
@@ -55,22 +54,27 @@ class Daemon:
|
|
|
55
54
|
signal(SIGINT, self.signal_handler) # ctrl+c
|
|
56
55
|
signal(SIGTERM, self.signal_handler) # systemctl stop
|
|
57
56
|
|
|
58
|
-
|
|
57
|
+
asyncio.run(self._run_workers())
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
async def _run_workers(self):
|
|
60
|
+
await asyncio.gather(
|
|
61
|
+
self.send_message_worker(),
|
|
62
|
+
self.read_pipe_loop(),
|
|
63
|
+
)
|
|
62
64
|
|
|
63
|
-
def read_pipe_loop(self
|
|
65
|
+
async def read_pipe_loop(self):
|
|
66
|
+
LOGGER.info(f"Daemon is listening for input on {self.pipe_file_path}...")
|
|
64
67
|
while True:
|
|
65
68
|
LOGGER.debug("Waiting for new input on pipe")
|
|
66
|
-
|
|
69
|
+
|
|
70
|
+
async with aiofiles.open(self.pipe_file_path, 'r') as pipe:
|
|
67
71
|
pipe_buffer = ''
|
|
68
72
|
while True:
|
|
69
|
-
data = pipe.read()
|
|
73
|
+
data = await pipe.read()
|
|
70
74
|
LOGGER.debug(f"received: {data}")
|
|
71
75
|
if len(data) == 0 and self.running:
|
|
72
76
|
LOGGER.debug("No new data from pipe, adding current buffer to message queue")
|
|
73
|
-
self.message_queue.put(pipe_buffer)
|
|
77
|
+
await self.message_queue.put(pipe_buffer)
|
|
74
78
|
break
|
|
75
79
|
elif not self.running:
|
|
76
80
|
break
|
|
@@ -80,21 +84,23 @@ class Daemon:
|
|
|
80
84
|
if not self.running:
|
|
81
85
|
break
|
|
82
86
|
|
|
83
|
-
def send_message_worker(self):
|
|
87
|
+
async def send_message_worker(self):
|
|
84
88
|
while True:
|
|
85
|
-
message
|
|
89
|
+
LOGGER.debug("Waiting for item in message queue...")
|
|
90
|
+
message = await self.message_queue.get()
|
|
91
|
+
LOGGER.debug(f"Got new message: {message}")
|
|
86
92
|
|
|
87
93
|
if not message.strip():
|
|
88
94
|
LOGGER.warning("Message is empty, ignoring")
|
|
89
95
|
self.message_queue.task_done()
|
|
90
96
|
continue
|
|
91
97
|
|
|
92
|
-
prepared_message = prepare_code_message(message
|
|
98
|
+
prepared_message = prepare_code_message(message)
|
|
93
99
|
LOGGER.debug(f"Trying to send message from queue...")
|
|
94
100
|
|
|
95
101
|
try:
|
|
96
|
-
|
|
97
|
-
|
|
102
|
+
await try_send_message(
|
|
103
|
+
app=self._app,
|
|
98
104
|
chat_id=self.config.TELEGRAM_CHAT_ID.value,
|
|
99
105
|
message=prepared_message,
|
|
100
106
|
retry=self.config.RETRY_ENABLED.value,
|
|
@@ -120,7 +126,8 @@ class Daemon:
|
|
|
120
126
|
LOGGER.warning("Unable to clean up named pipe")
|
|
121
127
|
|
|
122
128
|
stop = time() + 5
|
|
123
|
-
|
|
129
|
+
|
|
130
|
+
while self.message_queue._unfinished_tasks and time() < stop:
|
|
124
131
|
sleep(1)
|
|
125
132
|
|
|
126
133
|
sys.exit(0)
|
|
@@ -18,7 +18,7 @@ import time
|
|
|
18
18
|
from datetime import datetime, timedelta
|
|
19
19
|
|
|
20
20
|
from telegram import Bot, InlineKeyboardMarkup, Message
|
|
21
|
-
from telegram.ext import
|
|
21
|
+
from telegram.ext import Application
|
|
22
22
|
|
|
23
23
|
from cli2telegram import RetryLimitReachedException
|
|
24
24
|
from cli2telegram.const import CODEBLOCK_MARKER_START, CODEBLOCK_MARKER_END
|
|
@@ -26,7 +26,7 @@ from cli2telegram.const import CODEBLOCK_MARKER_START, CODEBLOCK_MARKER_END
|
|
|
26
26
|
LOGGER = logging.getLogger(__name__)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def send_message(
|
|
29
|
+
async def send_message(
|
|
30
30
|
bot: Bot, chat_id: str, message: str, parse_mode: str = None, reply_to: int = None,
|
|
31
31
|
menu: InlineKeyboardMarkup = None
|
|
32
32
|
) -> Message:
|
|
@@ -40,10 +40,10 @@ def send_message(
|
|
|
40
40
|
:param menu: inline keyboard menu markup
|
|
41
41
|
"""
|
|
42
42
|
from emoji import emojize
|
|
43
|
-
emojized_text = emojize(message,
|
|
44
|
-
return bot.send_message(
|
|
43
|
+
emojized_text = emojize(message, language='alias')
|
|
44
|
+
return await bot.send_message(
|
|
45
45
|
chat_id=chat_id, parse_mode=parse_mode, text=emojized_text, reply_to_message_id=reply_to, reply_markup=menu,
|
|
46
|
-
|
|
46
|
+
connect_timeout=10, read_timeout=10, write_timeout=10
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
|
|
@@ -73,14 +73,14 @@ def prepare_code_message(message: str) -> str:
|
|
|
73
73
|
return result
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
def
|
|
77
|
-
|
|
76
|
+
async def try_send_message(
|
|
77
|
+
app: Application,
|
|
78
78
|
chat_id: str, message: str,
|
|
79
79
|
retry: bool, retry_timeout: timedelta, give_up_after: timedelta
|
|
80
80
|
):
|
|
81
81
|
"""
|
|
82
82
|
Sends a message
|
|
83
|
-
:param
|
|
83
|
+
:param app: telegram application
|
|
84
84
|
:param chat_id: chat id
|
|
85
85
|
:param message: the message to send
|
|
86
86
|
:param retry: whether to retry if something fails
|
|
@@ -91,8 +91,7 @@ def _try_send_message(
|
|
|
91
91
|
success = False
|
|
92
92
|
while not success:
|
|
93
93
|
try:
|
|
94
|
-
|
|
95
|
-
send_message(bot=updater.bot, chat_id=chat_id, message=message, parse_mode="markdown")
|
|
94
|
+
await send_message(bot=app.bot, chat_id=chat_id, message=message, parse_mode="markdown")
|
|
96
95
|
success = True
|
|
97
96
|
except Exception as ex:
|
|
98
97
|
LOGGER.exception(ex)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "cli2telegram"
|
|
3
|
+
version = "2.0.2"
|
|
4
|
+
description = "Small utility to send Telegram messages from the CLI."
|
|
5
|
+
|
|
6
|
+
license = "AGPL-3.0-or-later"
|
|
7
|
+
|
|
8
|
+
authors = [
|
|
9
|
+
"Markus Ressel <mail@markusressel.de>",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
readme = 'README.md'
|
|
13
|
+
|
|
14
|
+
repository = "https://github.com/markusressel/cli2telegram"
|
|
15
|
+
homepage = "https://github.com/markusressel/cli2telegram"
|
|
16
|
+
|
|
17
|
+
keywords = ['cli', 'telegram', 'notification']
|
|
18
|
+
|
|
19
|
+
classifiers = [
|
|
20
|
+
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
|
|
21
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
"Development Status :: 5 - Production/Stable"
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["poetry-core>=1.0.0"]
|
|
31
|
+
build-backend = "poetry.core.masonry.api"
|
|
32
|
+
|
|
33
|
+
[tool.poetry.dependencies]
|
|
34
|
+
python = "^3.12" # Compatible python versions must be declared here
|
|
35
|
+
|
|
36
|
+
container-app-conf = "*"
|
|
37
|
+
click = "*"
|
|
38
|
+
emoji = "*"
|
|
39
|
+
python-telegram-bot = "*"
|
|
40
|
+
aiofiles = "*"
|
|
41
|
+
|
|
42
|
+
[tool.poetry.dev-dependencies]
|
|
43
|
+
pytest = "*"
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
testpaths = [
|
|
47
|
+
"tests",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.poetry.scripts]
|
|
51
|
+
cli2telegram = 'cli2telegram.cli:cli'
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: cli2telegram
|
|
3
|
-
Version: 2.0.0
|
|
4
|
-
Summary: Small utility to send Telegram messages from the CLI.
|
|
5
|
-
Home-page: https://github.com/markusressel/cli2telegram
|
|
6
|
-
Author: Markus Ressel
|
|
7
|
-
Author-email: mail@markusressel.de
|
|
8
|
-
License: AGPLv3+
|
|
9
|
-
Platform: UNKNOWN
|
|
10
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
-
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
|
12
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
License-File: LICENSE
|
|
21
|
-
|
|
22
|
-
# cli2telegram
|
|
23
|
-
Small utility to send Telegram messages from the CLI.
|
|
24
|
-
|
|
25
|
-
This can f.ex. be used to
|
|
26
|
-
* quickly send arbitrary messages to a Telegram chat of your choice
|
|
27
|
-
* use it as a replacement for the "mail" program on linux
|
|
28
|
-
* use Telegram as a notification backend for the ZFS Event Daemon (zed)
|
|
29
|
-
|
|
30
|
-
## Features
|
|
31
|
-
* [x] Read messages from argument or STDIN
|
|
32
|
-
* [x] (Optional) Configuration file
|
|
33
|
-
* [x] Retry sending messages for a specified amount of time
|
|
34
|
-
* [x] Handling of long messages
|
|
35
|
-
* [x] Run as a daemon and echo messages into a linux pipe
|
|
36
|
-
|
|
37
|
-
## Examples
|
|
38
|
-
|
|
39
|
-
```shell
|
|
40
|
-
cli2telegram -h
|
|
41
|
-
|
|
42
|
-
# From arguments
|
|
43
|
-
cli2telegram "This is a message"
|
|
44
|
-
|
|
45
|
-
cli2telegram "Header" "This is a multiline message."
|
|
46
|
-
|
|
47
|
-
# From STDIN
|
|
48
|
-
echo My Message | cli2telegram
|
|
49
|
-
|
|
50
|
-
printf "Header\nThis is a multiline message." | cli2telegram
|
|
51
|
-
|
|
52
|
-
# Config via parameters
|
|
53
|
-
printf "Message" | cli2telegram -b "123456789:ABCDEFG" -c "123456789"
|
|
54
|
-
|
|
55
|
-
# Config via parameters and send message in a code block
|
|
56
|
-
printf "Message" | cli2telegram -b "123456789:ABCDEFG" -c "123456789" -C
|
|
57
|
-
|
|
58
|
-
# as a Daemon
|
|
59
|
-
cli2telegram -d -p "/tmp/cli2telegram"
|
|
60
|
-
echo "hello world!" > /tmp/cli2telegram
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
# Install
|
|
64
|
-
|
|
65
|
-
To use this utility install it either using:
|
|
66
|
-
```
|
|
67
|
-
pip install cli2telegram
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
or - if you don't want to install it globally - using f.ex. [venv-install](https://github.com/markusressel/venv-install):
|
|
71
|
-
```
|
|
72
|
-
venv-install cli2telegram cli2telegram
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
or your custom `venv` manager of choice.
|
|
76
|
-
|
|
77
|
-
# Configuration
|
|
78
|
-
|
|
79
|
-
To be able to send you messages you have to provide a **bot token** and a **chat id**.
|
|
80
|
-
You can configure cli2telegram using cli parameters, a configuration file,
|
|
81
|
-
environment variables, or a combination of them.
|
|
82
|
-
|
|
83
|
-
## Parameters
|
|
84
|
-
|
|
85
|
-
| Name | Type | Description |
|
|
86
|
-
|----------------------|--------|-------------------------------------------|
|
|
87
|
-
| `-b`, `--bot-token` | String | Telegram Bot Token |
|
|
88
|
-
| `-c`, `--chat-id` | String | Telegram Chat ID |
|
|
89
|
-
| `-C`, `--code-block` | String | Send message inside a code block |
|
|
90
|
-
| `-d`, `--daemon` | Flag | Run as a daemon |
|
|
91
|
-
| `-p`, `--pipe` | String | File path to the pipe used in daemon mode |
|
|
92
|
-
|
|
93
|
-
## File
|
|
94
|
-
cli2telegram uses [container-app-conf](https://github.com/markusressel/container-app-conf) so you can use YAML, TOML, or ENV to set those.
|
|
95
|
-
|
|
96
|
-
Have a look at the [cli2telegram.toml_example](cli2telegram.toml_example) file to get an idea.
|
|
97
|
-
|
|
98
|
-
# Daemon
|
|
99
|
-
|
|
100
|
-
When running cli2telegram as a daemon, the pipe will close for a brief amount of time between receiving input messages.
|
|
101
|
-
If you are sending multiple messages to the pipe using f.ex. a script, make sure to wait a bit (f.ex. 0.5 seconds)
|
|
102
|
-
between sending messages, otherwise:
|
|
103
|
-
* multiple messages may be received as one
|
|
104
|
-
* messages may get lost
|
|
105
|
-
* you may receive a `Broken pipe` error
|
|
106
|
-
|
|
107
|
-
# Use Cases
|
|
108
|
-
|
|
109
|
-
## ZFS Event Daemon (ZED)
|
|
110
|
-
To make `zed` call cli2telegram we will trick it and make it use cli2telegram as an E-Mail client.
|
|
111
|
-
|
|
112
|
-
Edit `/etc/zfs/zed.d/zed.rc` as root:
|
|
113
|
-
```bash
|
|
114
|
-
sudo nano -w /etc/zfs/zed.d/zed.rc
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
and
|
|
118
|
-
* uncomment `ZED_EMAIL_ADDR`, the value does not matter since we use our own email script, but **it is necessary to set a value to make ZED send 'emails'**
|
|
119
|
-
* set `ZED_EMAIL_PROG` to the path of the script, f.ex. `/usr/bin/cli2telegram`
|
|
120
|
-
* it is important to note that zed does not seem to work if your command needs arguments to run
|
|
121
|
-
```
|
|
122
|
-
# this must not be empty!
|
|
123
|
-
ZED_EMAIL_ADDR="root"
|
|
124
|
-
|
|
125
|
-
[...]
|
|
126
|
-
|
|
127
|
-
ZED_EMAIL_PROG="/usr/bin/cli2telegram"
|
|
128
|
-
|
|
129
|
-
[...]
|
|
130
|
-
|
|
131
|
-
# this must not be empty!
|
|
132
|
-
ZED_EMAIL_OPTS="#zfs #$(hostname)"
|
|
133
|
-
|
|
134
|
-
[...]
|
|
135
|
-
|
|
136
|
-
# If you want to receive email no matter the state of your pool, you’ll want to set:
|
|
137
|
-
ZED_NOTIFY_VERBOSE=1
|
|
138
|
-
|
|
139
|
-
[...]
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Since `zed` will run your scripts as root, if you want to use a config file
|
|
143
|
-
you have to put it in f.ex. `/root/.config/cli2telegram.toml`.
|
|
144
|
-
|
|
145
|
-
# Contributing
|
|
146
|
-
|
|
147
|
-
GitHub is for social coding: if you want to write code, I encourage contributions through pull requests from forks
|
|
148
|
-
of this repository. Create GitHub tickets for bugs and new features and comment on the ones that you are interested in.
|
|
149
|
-
|
|
150
|
-
# License
|
|
151
|
-
|
|
152
|
-
```text
|
|
153
|
-
cli2telegram by Markus Ressel
|
|
154
|
-
Copyright (C) 2018 Markus Ressel
|
|
155
|
-
|
|
156
|
-
This program is free software: you can redistribute it and/or modify
|
|
157
|
-
it under the terms of the GNU Affero General Public License as
|
|
158
|
-
published by the Free Software Foundation, either version 3 of the
|
|
159
|
-
License, or (at your option) any later version.
|
|
160
|
-
|
|
161
|
-
This program is distributed in the hope that it will be useful,
|
|
162
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
163
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
164
|
-
GNU Affero General Public License for more details.
|
|
165
|
-
|
|
166
|
-
You should have received a copy of the GNU Affero General Public License
|
|
167
|
-
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
setup.py
|
|
4
|
-
cli2telegram/__init__.py
|
|
5
|
-
cli2telegram/cli.py
|
|
6
|
-
cli2telegram/config.py
|
|
7
|
-
cli2telegram/const.py
|
|
8
|
-
cli2telegram/daemon.py
|
|
9
|
-
cli2telegram/util.py
|
|
10
|
-
cli2telegram.egg-info/PKG-INFO
|
|
11
|
-
cli2telegram.egg-info/SOURCES.txt
|
|
12
|
-
cli2telegram.egg-info/dependency_links.txt
|
|
13
|
-
cli2telegram.egg-info/entry_points.txt
|
|
14
|
-
cli2telegram.egg-info/requires.txt
|
|
15
|
-
cli2telegram.egg-info/top_level.txt
|
|
16
|
-
tests/__init__.py
|
|
17
|
-
tests/test_message_processing.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
cli2telegram-2.0.0/setup.cfg
DELETED
cli2telegram-2.0.0/setup.py
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2019 Markus Ressel
|
|
2
|
-
#
|
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
-
# furnished to do so, subject to the following conditions:
|
|
9
|
-
#
|
|
10
|
-
# The above copyright notice and this permission notice shall be included in all
|
|
11
|
-
# copies or substantial portions of the Software.
|
|
12
|
-
#
|
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
-
# SOFTWARE.
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
from setuptools import setup, find_packages
|
|
23
|
-
|
|
24
|
-
VERSION_NUMBER = "2.0.0"
|
|
25
|
-
|
|
26
|
-
DEVELOPMENT_STATUS = "Development Status :: 5 - Production/Stable"
|
|
27
|
-
VERSION_NAME = VERSION_NUMBER
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def readme_type() -> str:
|
|
31
|
-
import os
|
|
32
|
-
if os.path.exists("README.rst"):
|
|
33
|
-
return "text/x-rst"
|
|
34
|
-
if os.path.exists("README.md"):
|
|
35
|
-
return "text/markdown"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def readme() -> [str]:
|
|
39
|
-
if readme_type() == "text/markdown":
|
|
40
|
-
file_name = "README.md"
|
|
41
|
-
else:
|
|
42
|
-
file_name = "README.rst"
|
|
43
|
-
|
|
44
|
-
with open(file_name) as f:
|
|
45
|
-
return f.read()
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def install_requirements() -> [str]:
|
|
49
|
-
return read_requirements_file("requirements.txt")
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def test_requirements() -> [str]:
|
|
53
|
-
return read_requirements_file("test_requirements.txt")
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def read_requirements_file(file_name: str):
|
|
57
|
-
with open(file_name, encoding='utf-8') as f:
|
|
58
|
-
requirements_file = f.readlines()
|
|
59
|
-
return [r.strip() for r in requirements_file]
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
setup(
|
|
63
|
-
name='cli2telegram',
|
|
64
|
-
version=VERSION_NAME,
|
|
65
|
-
description='Small utility to send Telegram messages from the CLI.',
|
|
66
|
-
long_description=readme(),
|
|
67
|
-
long_description_content_type=readme_type(),
|
|
68
|
-
license='AGPLv3+',
|
|
69
|
-
author='Markus Ressel',
|
|
70
|
-
author_email='mail@markusressel.de',
|
|
71
|
-
url='https://github.com/markusressel/cli2telegram',
|
|
72
|
-
packages=find_packages(),
|
|
73
|
-
classifiers=[
|
|
74
|
-
DEVELOPMENT_STATUS,
|
|
75
|
-
'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)',
|
|
76
|
-
'Programming Language :: Python :: 3 :: Only',
|
|
77
|
-
'Programming Language :: Python :: 3',
|
|
78
|
-
'Programming Language :: Python :: 3.6',
|
|
79
|
-
'Programming Language :: Python :: 3.7',
|
|
80
|
-
'Programming Language :: Python :: 3.8',
|
|
81
|
-
'Programming Language :: Python :: 3.9',
|
|
82
|
-
'Programming Language :: Python :: 3.10',
|
|
83
|
-
],
|
|
84
|
-
install_requires=install_requirements(),
|
|
85
|
-
tests_require=test_requirements(),
|
|
86
|
-
entry_points={
|
|
87
|
-
'console_scripts': ['cli2telegram = cli2telegram.cli:cli']
|
|
88
|
-
}
|
|
89
|
-
)
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# cli2telegram
|
|
2
|
-
# Copyright (c) 2019. Markus Ressel
|
|
3
|
-
#
|
|
4
|
-
# This program is free software: you can redistribute it and/or modify
|
|
5
|
-
# it under the terms of the GNU Affero General Public License as
|
|
6
|
-
# published by the Free Software Foundation, either version 3 of the
|
|
7
|
-
# License, or (at your option) any later version.
|
|
8
|
-
#
|
|
9
|
-
# This program is distributed in the hope that it will be useful,
|
|
10
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
-
# GNU Affero General Public License for more details.
|
|
13
|
-
#
|
|
14
|
-
# You should have received a copy of the GNU Affero General Public License
|
|
15
|
-
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
-
|
|
17
|
-
import unittest
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class TestBase(unittest.TestCase):
|
|
21
|
-
if __name__ == '__main__':
|
|
22
|
-
unittest.main()
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# cli2telegram
|
|
2
|
-
# Copyright (c) 2019. Markus Ressel
|
|
3
|
-
#
|
|
4
|
-
# This program is free software: you can redistribute it and/or modify
|
|
5
|
-
# it under the terms of the GNU Affero General Public License as
|
|
6
|
-
# published by the Free Software Foundation, either version 3 of the
|
|
7
|
-
# License, or (at your option) any later version.
|
|
8
|
-
#
|
|
9
|
-
# This program is distributed in the hope that it will be useful,
|
|
10
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
-
# GNU Affero General Public License for more details.
|
|
13
|
-
#
|
|
14
|
-
# You should have received a copy of the GNU Affero General Public License
|
|
15
|
-
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
-
from cli2telegram.cli import prepare_messages
|
|
17
|
-
from cli2telegram.util import prepare_code_message, split_message
|
|
18
|
-
from tests import TestBase
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class MessageProcessingTest(TestBase):
|
|
22
|
-
|
|
23
|
-
def test_long_message_with_codeblock_doesnt_exceed_limit(self):
|
|
24
|
-
std_in = "A" * 4096 + "B" * 4096
|
|
25
|
-
messages = prepare_messages(std_in, True)
|
|
26
|
-
|
|
27
|
-
assert len(messages) == 3
|
|
28
|
-
for m in messages:
|
|
29
|
-
assert len(m) <= 4096
|
|
30
|
-
assert "B" not in messages[0]
|
|
31
|
-
assert "A" in messages[1]
|
|
32
|
-
assert "B" in messages[1]
|
|
33
|
-
assert "A" not in messages[2]
|
|
34
|
-
|
|
35
|
-
def test_long_message_doesnt_exceed_limit(self):
|
|
36
|
-
std_in = "A" * 4096 + "B" * 4096
|
|
37
|
-
messages = prepare_messages(std_in, False)
|
|
38
|
-
|
|
39
|
-
assert len(messages) == 2
|
|
40
|
-
for i, m in enumerate(messages):
|
|
41
|
-
if i < len(messages) - 1:
|
|
42
|
-
assert len(m) == 4096
|
|
43
|
-
else:
|
|
44
|
-
assert len(m) <= 4096
|
|
45
|
-
assert "B" not in messages[0]
|
|
46
|
-
assert "A" not in messages[1]
|
|
47
|
-
|
|
48
|
-
def test_long_message_is_split(self):
|
|
49
|
-
length = 32
|
|
50
|
-
std_in = "A" * length + "B" * length
|
|
51
|
-
messages = split_message(std_in, length)
|
|
52
|
-
|
|
53
|
-
assert len(messages) == 2
|
|
54
|
-
for i, m in enumerate(messages):
|
|
55
|
-
if i < len(messages) - 1:
|
|
56
|
-
assert len(m) == length
|
|
57
|
-
else:
|
|
58
|
-
assert len(m) <= length
|
|
59
|
-
assert "B" not in messages[0]
|
|
60
|
-
assert "A" not in messages[1]
|
|
61
|
-
|
|
62
|
-
def test_prepare_code_message(self):
|
|
63
|
-
message = "Code"
|
|
64
|
-
output = prepare_code_message(message)
|
|
65
|
-
|
|
66
|
-
assert output.startswith("```\n")
|
|
67
|
-
assert output.endswith("\n```")
|
|
68
|
-
assert message[0] in output
|
|
69
|
-
|
|
70
|
-
def test_newlines_are_retained_in_code_block(self):
|
|
71
|
-
std_in = "\nCo\nde\n"
|
|
72
|
-
output = prepare_code_message(std_in)
|
|
73
|
-
|
|
74
|
-
assert output == "```\n\nCo\nde\n\n```"
|
|
75
|
-
|
|
76
|
-
def test_message_preparation(self):
|
|
77
|
-
std_in = """ZFS has finished a scrub:\n
|
|
78
|
-
\n
|
|
79
|
-
eid: 3337\n
|
|
80
|
-
class: scrub_finish\n
|
|
81
|
-
host: \n
|
|
82
|
-
time: 2019-12-01 19:36:01+0100\n
|
|
83
|
-
pool: rpool\n
|
|
84
|
-
state: ONLINE\n
|
|
85
|
-
scan: scrub repaired 0B in 0 days 00:00:24 with 0 errors on Sun Dec 1 19:36:01 2019\n
|
|
86
|
-
config:\n
|
|
87
|
-
\n
|
|
88
|
-
\tNAME STATE READ WRITE CKSUM\n
|
|
89
|
-
\trpool ONLINE 0 0 0\n
|
|
90
|
-
\t mirror-0 ONLINE 0 0 0\n
|
|
91
|
-
\t ata-Samsung_SSD_860_EVO_500GB_S4XBNF1M807031E-part2 ONLINE 0 0 0\n
|
|
92
|
-
\t ata-Samsung_SSD_860_EVO_500GB_S4XBNF1M807033B-part2 ONLINE 0 0 0\n
|
|
93
|
-
\n
|
|
94
|
-
errors: No known data errors\n"""
|
|
95
|
-
|
|
96
|
-
output = prepare_code_message(std_in)
|
|
97
|
-
assert output
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|