Skip to content

Commit 7bfbe18

Browse files
committed
init
0 parents  commit 7bfbe18

8 files changed

+704
-0
lines changed

.gitignore

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
share/python-wheels/
24+
*.egg-info/
25+
.installed.cfg
26+
*.egg
27+
MANIFEST
28+
29+
# PyInstaller
30+
# Usually these files are written by a python script from a template
31+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
32+
*.manifest
33+
*.spec
34+
35+
# Installer logs
36+
pip-log.txt
37+
pip-delete-this-directory.txt
38+
39+
# Unit test / coverage reports
40+
htmlcov/
41+
.tox/
42+
.nox/
43+
.coverage
44+
.coverage.*
45+
.cache
46+
nosetests.xml
47+
coverage.xml
48+
*.cover
49+
*.py,cover
50+
.hypothesis/
51+
.pytest_cache/
52+
cover/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
63+
64+
# Flask stuff:
65+
instance/
66+
.webassets-cache
67+
68+
# Scrapy stuff:
69+
.scrapy
70+
71+
# Sphinx documentation
72+
docs/_build/
73+
74+
# PyBuilder
75+
.pybuilder/
76+
target/
77+
78+
# Jupyter Notebook
79+
.ipynb_checkpoints
80+
81+
# IPython
82+
profile_default/
83+
ipython_config.py
84+
85+
# pyenv
86+
# For a library or package, you might want to ignore these files since the code is
87+
# intended to run in multiple environments; otherwise, check them in:
88+
# .python-version
89+
90+
# pipenv
91+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
93+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
94+
# install all needed dependencies.
95+
#Pipfile.lock
96+
97+
# poetry
98+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99+
# This is especially recommended for binary packages to ensure reproducibility, and is more
100+
# commonly ignored for libraries.
101+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102+
#poetry.lock
103+
104+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
105+
__pypackages__/
106+
107+
# Celery stuff
108+
celerybeat-schedule
109+
celerybeat.pid
110+
111+
# SageMath parsed files
112+
*.sage.py
113+
114+
# Environments
115+
.env
116+
.venv
117+
env/
118+
venv/
119+
ENV/
120+
env.bak/
121+
venv.bak/
122+
123+
# Spyder project settings
124+
.spyderproject
125+
.spyproject
126+
127+
# Rope project settings
128+
.ropeproject
129+
130+
# mkdocs documentation
131+
/site
132+
133+
# mypy
134+
.mypy_cache/
135+
.dmypy.json
136+
dmypy.json
137+
138+
# Pyre type checker
139+
.pyre/
140+
141+
# pytype static type analyzer
142+
.pytype/
143+
144+
# Cython debug symbols
145+
cython_debug/
146+
147+
# VS code workspaces
148+
*.code-workspace

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) [year] [fullname]
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.

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Post process information to the slack without clogging up the channel with a bunch of messages. Easyly update status, see when oreration started / finished and how much time it took.

setup.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from setuptools import setup, find_packages
2+
3+
VERSION = "0.0.4"
4+
DESCRIPTION = "Easyly post good looking slack messages about your operations"
5+
LONG_DESCRIPTION = "Post process information to the slack without clogging up the channel with a bunch of messages. Easyly update status, see when oreration started / finished and how much time it took."
6+
7+
setup(
8+
name="slackops",
9+
version=VERSION,
10+
author="Alexey Eremin",
11+
author_email="haru.eaa@gmail.com",
12+
description=DESCRIPTION,
13+
long_description=LONG_DESCRIPTION,
14+
packages=find_packages(),
15+
install_requires=["pytz", "slack_sdk"],
16+
)

slackops/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .interfaces import Operation, Message

slackops/blocks.py

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
from __future__ import annotations
2+
from slack_sdk.models.attachments import BlockAttachment
3+
from slack_sdk.models.blocks.blocks import (
4+
SectionBlock,
5+
ContextBlock,
6+
HeaderBlock,
7+
Block,
8+
DividerBlock,
9+
)
10+
from slack_sdk.models.blocks.basic_components import MarkdownTextObject
11+
12+
13+
class Message:
14+
def __init__(self, text, header="", context: list = None):
15+
self.text = text
16+
self.header = header
17+
self.context = context
18+
19+
20+
class Operation:
21+
def __init__(self, name: str, status: str, started: str, finished: str | None = ""):
22+
self.name = name
23+
self.status = status
24+
self.started = started
25+
self.finished = finished
26+
27+
def __bool__(self):
28+
return all([self.name, self.status, self.started])
29+
30+
31+
class BlockTemplate:
32+
@staticmethod
33+
def header(
34+
text: str | None = "", block_id: str | None = "header"
35+
) -> HeaderBlock | None:
36+
if text:
37+
return HeaderBlock(text=text, block_id=block_id)
38+
39+
@staticmethod
40+
def text(text: str = None, block_id: str | None = "text") -> SectionBlock | None:
41+
if text:
42+
return SectionBlock(block_id=block_id, text=MarkdownTextObject(text=text))
43+
44+
@staticmethod
45+
def operation(
46+
operation: Operation, block_id: str | None = "operation"
47+
) -> SectionBlock | None:
48+
if operation:
49+
fields = [
50+
MarkdownTextObject(text=operation.name),
51+
MarkdownTextObject(text=operation.started),
52+
MarkdownTextObject(text=operation.status),
53+
]
54+
if operation.finished:
55+
fields.append(MarkdownTextObject(text=operation.finished))
56+
57+
return SectionBlock(
58+
block_id=block_id,
59+
fields=fields,
60+
)
61+
62+
@staticmethod
63+
def context(
64+
context: list = None, block_id: str | None = "context"
65+
) -> ContextBlock | None:
66+
if context:
67+
return ContextBlock(
68+
block_id=block_id,
69+
elements=[MarkdownTextObject(text=element) for element in context],
70+
)
71+
72+
73+
class BlockComposition:
74+
@staticmethod
75+
def context(context: list = None) -> list[DividerBlock | ContextBlock | None]:
76+
if context:
77+
context = [
78+
DividerBlock(),
79+
BlockTemplate.context(context),
80+
]
81+
return context or []
82+
83+
@staticmethod
84+
def message(message: Message) -> list[Block | None]:
85+
blocks = [
86+
BlockTemplate.header(message.header),
87+
BlockTemplate.text(message.text),
88+
*BlockComposition.context(message.context),
89+
]
90+
return blocks
91+
92+
@staticmethod
93+
def operation(operation: Operation, message: Message) -> list[Block | None]:
94+
blocks = [
95+
BlockTemplate.header(message.header),
96+
BlockTemplate.text(message.text),
97+
BlockTemplate.operation(operation),
98+
*BlockComposition.context(message.context),
99+
]
100+
return blocks
101+
102+
103+
class AttachmentTemplate:
104+
@staticmethod
105+
def attachment(
106+
blocks: list, color: str = None, fallback: str = None
107+
) -> BlockAttachment:
108+
return BlockAttachment(blocks=filter(None, blocks), color=color, fallback=fallback) # type: ignore
109+
110+
@staticmethod
111+
def message(
112+
text: str = None,
113+
color: str = None,
114+
header: str = None,
115+
context: list = None,
116+
) -> BlockAttachment:
117+
blocks = BlockComposition.message(Message(text, header, context))
118+
fallback = text or header
119+
return AttachmentTemplate.attachment(
120+
blocks=blocks, color=color, fallback=fallback
121+
)
122+
123+
@staticmethod
124+
def operation(
125+
name: str = "",
126+
status: str = "",
127+
started: str = "",
128+
header: str = None,
129+
text: str = None,
130+
context: list = None,
131+
color: str = None,
132+
finished: str = None,
133+
) -> BlockAttachment:
134+
blocks = BlockComposition.operation(
135+
Operation(name, status, started, finished), Message(text, header, context)
136+
)
137+
fallback = status or text or header
138+
139+
return AttachmentTemplate.attachment(
140+
blocks=blocks, color=color, fallback=fallback
141+
)

0 commit comments

Comments
 (0)