script-reporter 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yoonbae Cho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: script-reporter
3
+ Version: 0.1.0
4
+ Summary: A lightweight status reporter for Python scripts with Discord and Telegram support
5
+ Author-email: Yoonbae Cho <y@xcv.kr>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/yoonbae81/script-reporter
8
+ Project-URL: Bug Tracker, https://github.com/yoonbae81/script-reporter/issues
9
+ Keywords: reporter,notification,discord,telegram,script,monitoring
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Requires-Python: >=3.7
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # script-reporter 🚀
20
+
21
+ A lightweight, zero-dependency Python status reporter designed for automation scripts and cron jobs. Easily report execution progress, stages, and results to **Console**, **Discord**, and **Telegram**.
22
+
23
+ ## ✨ Key Features
24
+
25
+ - **Zero External Dependencies**: Uses Python standard library only (`urllib`).
26
+ - **Multi-Channel**: Simultaneous reporting to Console, Discord (Webhooks), and Telegram (Bot API).
27
+ - **Stage Tracking**: Monitor exactly where your script is (e.g., `SETUP` -> `PROCESSING` -> `COMPLETE`).
28
+ - **Error Transparency**: Automatically attaches tracebacks on failure.
29
+ - **Duration Tracking**: Automatically calculates and reports execution time.
30
+ - **CI/CD & Cron Friendly**: Standardized JSON output for logs makes it easy for other tools to parse.
31
+
32
+ ## 📦 Installation
33
+
34
+ ```bash
35
+ pip install script-reporter
36
+ ```
37
+
38
+ ## 🚀 Quick Start
39
+
40
+ ### 1. Simple Usage (Auto-reporitng)
41
+
42
+ Set simple environment variables to enable channels:
43
+ - `REPORTER_DISCORD_WEBHOOK`: Discord Webhook URL
44
+ - `REPORTER_TELEGRAM_TOKEN` & `REPORTER_TELEGRAM_CHAT_ID`: Telegram Bot Token and Chat ID
45
+
46
+ ```python
47
+ from script_reporter import ScriptReporter
48
+ import traceback
49
+
50
+ # Initialize with a title (auto-detects Discord/Telegram from Env)
51
+ reporter = ScriptReporter("My Automation Task")
52
+
53
+ try:
54
+ # 1. Start a stage
55
+ reporter.stage("DATA_FETCH")
56
+ # ... your logic ...
57
+
58
+ # 2. Update stage as you progress
59
+ reporter.stage("PROCESSING")
60
+ # ... your logic ...
61
+
62
+ # 3. Report success with optional detail dictionary
63
+ reporter.success({"items_processed": 100, "status": "all_good"})
64
+
65
+ except Exception:
66
+ # 4. Report failure with traceback
67
+ reporter.fail(traceback.format_exc())
68
+ ```
69
+
70
+ ### 2. Manual Configuration
71
+
72
+ Explicitly control adapters without environment variables:
73
+
74
+ ```python
75
+ from script_reporter import ScriptReporter, ConsoleAdapter, DiscordAdapter, TelegramAdapter
76
+
77
+ adapters = [
78
+ ConsoleAdapter(),
79
+ DiscordAdapter(webhook_url="https://discord.com/api/webhooks/..."),
80
+ TelegramAdapter(token="BOT_TOKEN", chat_id="CHAT_ID")
81
+ ]
82
+
83
+ reporter = ScriptReporter("Manual Config Task", adapters=adapters)
84
+ reporter.success({"msg": "Hello from code!"})
85
+ ```
86
+
87
+ ## 📊 JSON Log Format
88
+
89
+ `ConsoleAdapter` prints a machine-readable JSON line starting with `__RESULT__`:
90
+
91
+ ```json
92
+ __RESULT__ {"title": "My Task", "host": "server-01", "stage": "COMPLETE", "status": "SUCCESS", "duration": "1m 30s", "detail": {"processed": 5}}
93
+ ```
94
+
95
+ ## 🛠 Advanced Concepts
96
+
97
+ - **Stages**: Use `reporter.stage("NAME")` to track progress. If `reporter.fail()` is called, it identifies the last successful stage.
98
+ - **Duration**: Tracks time from `ScriptReporter()` initialization to `success()`/`fail()` call.
99
+ - **Discord Rich Embeds**: `DiscordAdapter` automatically formats details and errors into beautiful rich embeds.
100
+
101
+ ## 📄 License
102
+
103
+ MIT License. See [LICENSE](LICENSE) for more details.
@@ -0,0 +1,85 @@
1
+ # script-reporter 🚀
2
+
3
+ A lightweight, zero-dependency Python status reporter designed for automation scripts and cron jobs. Easily report execution progress, stages, and results to **Console**, **Discord**, and **Telegram**.
4
+
5
+ ## ✨ Key Features
6
+
7
+ - **Zero External Dependencies**: Uses Python standard library only (`urllib`).
8
+ - **Multi-Channel**: Simultaneous reporting to Console, Discord (Webhooks), and Telegram (Bot API).
9
+ - **Stage Tracking**: Monitor exactly where your script is (e.g., `SETUP` -> `PROCESSING` -> `COMPLETE`).
10
+ - **Error Transparency**: Automatically attaches tracebacks on failure.
11
+ - **Duration Tracking**: Automatically calculates and reports execution time.
12
+ - **CI/CD & Cron Friendly**: Standardized JSON output for logs makes it easy for other tools to parse.
13
+
14
+ ## 📦 Installation
15
+
16
+ ```bash
17
+ pip install script-reporter
18
+ ```
19
+
20
+ ## 🚀 Quick Start
21
+
22
+ ### 1. Simple Usage (Auto-reporitng)
23
+
24
+ Set simple environment variables to enable channels:
25
+ - `REPORTER_DISCORD_WEBHOOK`: Discord Webhook URL
26
+ - `REPORTER_TELEGRAM_TOKEN` & `REPORTER_TELEGRAM_CHAT_ID`: Telegram Bot Token and Chat ID
27
+
28
+ ```python
29
+ from script_reporter import ScriptReporter
30
+ import traceback
31
+
32
+ # Initialize with a title (auto-detects Discord/Telegram from Env)
33
+ reporter = ScriptReporter("My Automation Task")
34
+
35
+ try:
36
+ # 1. Start a stage
37
+ reporter.stage("DATA_FETCH")
38
+ # ... your logic ...
39
+
40
+ # 2. Update stage as you progress
41
+ reporter.stage("PROCESSING")
42
+ # ... your logic ...
43
+
44
+ # 3. Report success with optional detail dictionary
45
+ reporter.success({"items_processed": 100, "status": "all_good"})
46
+
47
+ except Exception:
48
+ # 4. Report failure with traceback
49
+ reporter.fail(traceback.format_exc())
50
+ ```
51
+
52
+ ### 2. Manual Configuration
53
+
54
+ Explicitly control adapters without environment variables:
55
+
56
+ ```python
57
+ from script_reporter import ScriptReporter, ConsoleAdapter, DiscordAdapter, TelegramAdapter
58
+
59
+ adapters = [
60
+ ConsoleAdapter(),
61
+ DiscordAdapter(webhook_url="https://discord.com/api/webhooks/..."),
62
+ TelegramAdapter(token="BOT_TOKEN", chat_id="CHAT_ID")
63
+ ]
64
+
65
+ reporter = ScriptReporter("Manual Config Task", adapters=adapters)
66
+ reporter.success({"msg": "Hello from code!"})
67
+ ```
68
+
69
+ ## 📊 JSON Log Format
70
+
71
+ `ConsoleAdapter` prints a machine-readable JSON line starting with `__RESULT__`:
72
+
73
+ ```json
74
+ __RESULT__ {"title": "My Task", "host": "server-01", "stage": "COMPLETE", "status": "SUCCESS", "duration": "1m 30s", "detail": {"processed": 5}}
75
+ ```
76
+
77
+ ## 🛠 Advanced Concepts
78
+
79
+ - **Stages**: Use `reporter.stage("NAME")` to track progress. If `reporter.fail()` is called, it identifies the last successful stage.
80
+ - **Duration**: Tracks time from `ScriptReporter()` initialization to `success()`/`fail()` call.
81
+ - **Discord Rich Embeds**: `DiscordAdapter` automatically formats details and errors into beautiful rich embeds.
82
+
83
+ ## 📄 License
84
+
85
+ MIT License. See [LICENSE](LICENSE) for more details.
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "script-reporter"
7
+ version = "0.1.0"
8
+ authors = [
9
+ { name = "Yoonbae Cho", email = "y@xcv.kr" },
10
+ ]
11
+ description = "A lightweight status reporter for Python scripts with Discord and Telegram support"
12
+ readme = "README.md"
13
+ requires-python = ">=3.7"
14
+ keywords = ["reporter", "notification", "discord", "telegram", "script", "monitoring"]
15
+ license = { text = "MIT" }
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ ]
22
+
23
+ [project.urls]
24
+ "Homepage" = "https://github.com/yoonbae81/script-reporter"
25
+ "Bug Tracker" = "https://github.com/yoonbae81/script-reporter/issues"
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from .reporter import ScriptReporter, ConsoleAdapter, DiscordAdapter, TelegramAdapter
2
+
3
+ __all__ = ['ScriptReporter', 'ConsoleAdapter', 'DiscordAdapter', 'TelegramAdapter']
@@ -0,0 +1,6 @@
1
+ from .base import BaseAdapter
2
+ from .console import ConsoleAdapter
3
+ from .discord import DiscordAdapter
4
+ from .telegram import TelegramAdapter
5
+
6
+ __all__ = ['BaseAdapter', 'ConsoleAdapter', 'DiscordAdapter', 'TelegramAdapter']
@@ -0,0 +1,3 @@
1
+ class BaseAdapter:
2
+ def send(self, data):
3
+ raise NotImplementedError
@@ -0,0 +1,6 @@
1
+ from .base import BaseAdapter
2
+
3
+ class ConsoleAdapter(BaseAdapter):
4
+ def send(self, data):
5
+ import json
6
+ print(f"__RESULT__ {json.dumps(data, ensure_ascii=False)}")
@@ -0,0 +1,50 @@
1
+ import json
2
+ import os
3
+ import time
4
+ import urllib.request
5
+ from .base import BaseAdapter
6
+
7
+ class DiscordAdapter(BaseAdapter):
8
+ def __init__(self, webhook_url=None):
9
+ self.webhook_url = webhook_url or os.environ.get('REPORTER_DISCORD_WEBHOOK')
10
+
11
+ def send(self, result):
12
+ if not self.webhook_url:
13
+ return
14
+
15
+ status_emoji = "" # Removed noisy emojis
16
+ title = result.get('title', 'Execution')
17
+
18
+ embed = {
19
+ "title": f"{title} Result: {result['status']}",
20
+ "color": 0x00ff00 if result['status'] == "SUCCESS" else 0xff0000,
21
+ "fields": [
22
+ {"name": "Host", "value": result['host'], "inline": True},
23
+ {"name": "Stage", "value": result['stage'], "inline": True},
24
+ {"name": "Duration", "value": result['duration'], "inline": True},
25
+ ],
26
+ "timestamp": time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
27
+ }
28
+
29
+ if result.get('detail'):
30
+ detail_lines = []
31
+ for key, value in result['detail'].items():
32
+ label = key.replace('_', ' ').title()
33
+ detail_lines.append(f"**{label}**: {value}")
34
+ if detail_lines:
35
+ embed["description"] = "\n".join(detail_lines)
36
+
37
+ if result['status'] == "FAIL" and result.get('trace'):
38
+ trace = result['trace']
39
+ if len(trace) > 1000:
40
+ trace = trace[:1000] + "..."
41
+ embed["fields"].append({"name": "Error Trace", "value": f"```python\n{trace}\n```"})
42
+
43
+ data = json.dumps({"embeds": [embed]}).encode('utf-8')
44
+ req = urllib.request.Request(self.webhook_url, data=data, headers={'Content-Type': 'application/json', 'User-Agent': 'Result-Bot'})
45
+
46
+ try:
47
+ with urllib.request.urlopen(req, timeout=10) as response:
48
+ pass
49
+ except Exception as e:
50
+ print(f"Failed to send Discord notification: {e}")
@@ -0,0 +1,46 @@
1
+ import json
2
+ import os
3
+ import urllib.request
4
+ from .base import BaseAdapter
5
+
6
+ class TelegramAdapter(BaseAdapter):
7
+ def __init__(self, token=None, chat_id=None):
8
+ self.token = token or os.environ.get('REPORTER_TELEGRAM_TOKEN')
9
+ self.chat_id = chat_id or os.environ.get('REPORTER_TELEGRAM_CHAT_ID')
10
+
11
+ def send(self, result):
12
+ if not self.token or not self.chat_id:
13
+ return
14
+
15
+ status_emoji = "" # Removed noisy emojis
16
+ title = result.get('title', 'Execution')
17
+ text = f"*{title} Result: {result['status']}*\n\n"
18
+ text += f"- *Host:* {result['host']}\n"
19
+ text += f"- *Stage:* {result['stage']}\n"
20
+ text += f"- *Duration:* {result['duration']}\n"
21
+
22
+ if result.get('detail'):
23
+ for key, value in result['detail'].items():
24
+ label = key.replace('_', ' ').title()
25
+ text += f"- *{label}:* {value}\n"
26
+
27
+ if result['status'] == "FAIL" and result.get('trace'):
28
+ trace = result['trace']
29
+ if len(trace) > 500:
30
+ trace = trace[:500] + "..."
31
+ text += f"\n*Error Trace:*\n`{trace}`"
32
+
33
+ url = f"https://api.telegram.org/bot{self.token}/sendMessage"
34
+ data = json.dumps({
35
+ "chat_id": self.chat_id,
36
+ "text": text,
37
+ "parse_mode": "Markdown"
38
+ }).encode('utf-8')
39
+
40
+ req = urllib.request.Request(url, data=data, headers={'Content-Type': 'application/json'})
41
+
42
+ try:
43
+ with urllib.request.urlopen(req, timeout=10) as response:
44
+ pass
45
+ except Exception as e:
46
+ print(f"Failed to send Telegram notification: {e}")
@@ -0,0 +1,67 @@
1
+ import json
2
+ import socket
3
+ import time
4
+ import os
5
+ from .adapters import ConsoleAdapter, DiscordAdapter, TelegramAdapter
6
+
7
+ class ScriptReporter:
8
+ def __init__(self, title="Task", adapters=None):
9
+ self.title = title
10
+ self.host = socket.gethostname()
11
+ self.start_time = time.time()
12
+ self.current_stage = "INIT"
13
+
14
+ if adapters is None:
15
+ # Default adapters: Console always
16
+ self.adapters = [ConsoleAdapter()]
17
+
18
+ # Auto-detect Discord
19
+ if os.environ.get('REPORTER_DISCORD_WEBHOOK'):
20
+ self.adapters.append(DiscordAdapter())
21
+
22
+ # Auto-detect Telegram
23
+ if os.environ.get('REPORTER_TELEGRAM_TOKEN') and os.environ.get('REPORTER_TELEGRAM_CHAT_ID'):
24
+ self.adapters.append(TelegramAdapter())
25
+ else:
26
+ self.adapters = adapters
27
+
28
+ def stage(self, stage_name):
29
+ self.current_stage = stage_name
30
+
31
+ def _get_duration(self):
32
+ duration_sec = time.time() - self.start_time
33
+ if duration_sec < 60:
34
+ return f"{int(duration_sec)}s"
35
+ else:
36
+ minutes = int(duration_sec // 60)
37
+ seconds = int(duration_sec % 60)
38
+ return f"{minutes}m {seconds}s"
39
+
40
+ def _notify(self, result_data):
41
+ for adapter in self.adapters:
42
+ try:
43
+ adapter.send(result_data)
44
+ except Exception as e:
45
+ print(f"Adapter {adapter.__class__.__name__} failed: {e}")
46
+
47
+ def success(self, detail=None):
48
+ result_data = {
49
+ "title": self.title,
50
+ "host": self.host,
51
+ "stage": "COMPLETE",
52
+ "status": "SUCCESS",
53
+ "duration": self._get_duration(),
54
+ "detail": detail or {}
55
+ }
56
+ self._notify(result_data)
57
+
58
+ def fail(self, error_trace):
59
+ result_data = {
60
+ "title": self.title,
61
+ "host": self.host,
62
+ "stage": self.current_stage,
63
+ "status": "FAIL",
64
+ "duration": self._get_duration(),
65
+ "trace": error_trace
66
+ }
67
+ self._notify(result_data)
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: script-reporter
3
+ Version: 0.1.0
4
+ Summary: A lightweight status reporter for Python scripts with Discord and Telegram support
5
+ Author-email: Yoonbae Cho <y@xcv.kr>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/yoonbae81/script-reporter
8
+ Project-URL: Bug Tracker, https://github.com/yoonbae81/script-reporter/issues
9
+ Keywords: reporter,notification,discord,telegram,script,monitoring
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Requires-Python: >=3.7
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # script-reporter 🚀
20
+
21
+ A lightweight, zero-dependency Python status reporter designed for automation scripts and cron jobs. Easily report execution progress, stages, and results to **Console**, **Discord**, and **Telegram**.
22
+
23
+ ## ✨ Key Features
24
+
25
+ - **Zero External Dependencies**: Uses Python standard library only (`urllib`).
26
+ - **Multi-Channel**: Simultaneous reporting to Console, Discord (Webhooks), and Telegram (Bot API).
27
+ - **Stage Tracking**: Monitor exactly where your script is (e.g., `SETUP` -> `PROCESSING` -> `COMPLETE`).
28
+ - **Error Transparency**: Automatically attaches tracebacks on failure.
29
+ - **Duration Tracking**: Automatically calculates and reports execution time.
30
+ - **CI/CD & Cron Friendly**: Standardized JSON output for logs makes it easy for other tools to parse.
31
+
32
+ ## 📦 Installation
33
+
34
+ ```bash
35
+ pip install script-reporter
36
+ ```
37
+
38
+ ## 🚀 Quick Start
39
+
40
+ ### 1. Simple Usage (Auto-reporitng)
41
+
42
+ Set simple environment variables to enable channels:
43
+ - `REPORTER_DISCORD_WEBHOOK`: Discord Webhook URL
44
+ - `REPORTER_TELEGRAM_TOKEN` & `REPORTER_TELEGRAM_CHAT_ID`: Telegram Bot Token and Chat ID
45
+
46
+ ```python
47
+ from script_reporter import ScriptReporter
48
+ import traceback
49
+
50
+ # Initialize with a title (auto-detects Discord/Telegram from Env)
51
+ reporter = ScriptReporter("My Automation Task")
52
+
53
+ try:
54
+ # 1. Start a stage
55
+ reporter.stage("DATA_FETCH")
56
+ # ... your logic ...
57
+
58
+ # 2. Update stage as you progress
59
+ reporter.stage("PROCESSING")
60
+ # ... your logic ...
61
+
62
+ # 3. Report success with optional detail dictionary
63
+ reporter.success({"items_processed": 100, "status": "all_good"})
64
+
65
+ except Exception:
66
+ # 4. Report failure with traceback
67
+ reporter.fail(traceback.format_exc())
68
+ ```
69
+
70
+ ### 2. Manual Configuration
71
+
72
+ Explicitly control adapters without environment variables:
73
+
74
+ ```python
75
+ from script_reporter import ScriptReporter, ConsoleAdapter, DiscordAdapter, TelegramAdapter
76
+
77
+ adapters = [
78
+ ConsoleAdapter(),
79
+ DiscordAdapter(webhook_url="https://discord.com/api/webhooks/..."),
80
+ TelegramAdapter(token="BOT_TOKEN", chat_id="CHAT_ID")
81
+ ]
82
+
83
+ reporter = ScriptReporter("Manual Config Task", adapters=adapters)
84
+ reporter.success({"msg": "Hello from code!"})
85
+ ```
86
+
87
+ ## 📊 JSON Log Format
88
+
89
+ `ConsoleAdapter` prints a machine-readable JSON line starting with `__RESULT__`:
90
+
91
+ ```json
92
+ __RESULT__ {"title": "My Task", "host": "server-01", "stage": "COMPLETE", "status": "SUCCESS", "duration": "1m 30s", "detail": {"processed": 5}}
93
+ ```
94
+
95
+ ## 🛠 Advanced Concepts
96
+
97
+ - **Stages**: Use `reporter.stage("NAME")` to track progress. If `reporter.fail()` is called, it identifies the last successful stage.
98
+ - **Duration**: Tracks time from `ScriptReporter()` initialization to `success()`/`fail()` call.
99
+ - **Discord Rich Embeds**: `DiscordAdapter` automatically formats details and errors into beautiful rich embeds.
100
+
101
+ ## 📄 License
102
+
103
+ MIT License. See [LICENSE](LICENSE) for more details.
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/script_reporter/__init__.py
5
+ src/script_reporter/reporter.py
6
+ src/script_reporter.egg-info/PKG-INFO
7
+ src/script_reporter.egg-info/SOURCES.txt
8
+ src/script_reporter.egg-info/dependency_links.txt
9
+ src/script_reporter.egg-info/top_level.txt
10
+ src/script_reporter/adapters/__init__.py
11
+ src/script_reporter/adapters/base.py
12
+ src/script_reporter/adapters/console.py
13
+ src/script_reporter/adapters/discord.py
14
+ src/script_reporter/adapters/telegram.py
@@ -0,0 +1 @@
1
+ script_reporter