django-slack-tools 0.2.2__py3-none-any.whl → 0.3.1__py3-none-any.whl
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.
- django_slack_tools/__init__.py +0 -1
- django_slack_tools/app_settings.py +54 -90
- django_slack_tools/locale/ko_KR/LC_MESSAGES/django.po +150 -186
- django_slack_tools/py.typed +0 -0
- django_slack_tools/slack_messages/admin/message.py +1 -52
- django_slack_tools/slack_messages/admin/message_recipient.py +2 -2
- django_slack_tools/slack_messages/admin/messaging_policy.py +5 -5
- django_slack_tools/slack_messages/backends/__init__.py +3 -3
- django_slack_tools/slack_messages/backends/base.py +39 -203
- django_slack_tools/slack_messages/backends/dummy.py +0 -12
- django_slack_tools/slack_messages/backends/{logging.py → logging_.py} +0 -6
- django_slack_tools/slack_messages/backends/slack.py +21 -75
- django_slack_tools/slack_messages/message_templates/__init__.py +5 -0
- django_slack_tools/slack_messages/message_templates/base.py +17 -0
- django_slack_tools/{utils/template → slack_messages/message_templates}/django.py +30 -25
- django_slack_tools/slack_messages/message_templates/python.py +38 -0
- django_slack_tools/slack_messages/messenger.py +158 -0
- django_slack_tools/slack_messages/middlewares/__init__.py +4 -0
- django_slack_tools/slack_messages/middlewares/base.py +34 -0
- django_slack_tools/slack_messages/middlewares/django.py +218 -0
- django_slack_tools/slack_messages/migrations/0001_initial.py +4 -4
- django_slack_tools/slack_messages/migrations/0005_alter_slackmessagingpolicy_template_type.py +46 -0
- django_slack_tools/slack_messages/migrations/0006_alter_slackmessage_id.py +26 -0
- django_slack_tools/slack_messages/models/mention.py +1 -1
- django_slack_tools/slack_messages/models/message.py +5 -2
- django_slack_tools/slack_messages/models/message_recipient.py +1 -1
- django_slack_tools/slack_messages/models/messaging_policy.py +4 -4
- django_slack_tools/slack_messages/request.py +91 -0
- django_slack_tools/slack_messages/response.py +21 -0
- django_slack_tools/slack_messages/shortcuts.py +78 -0
- django_slack_tools/slack_messages/tasks.py +8 -57
- django_slack_tools/slack_messages/template_loaders/__init__.py +11 -0
- django_slack_tools/slack_messages/template_loaders/base.py +16 -0
- django_slack_tools/slack_messages/template_loaders/django.py +74 -0
- django_slack_tools/slack_messages/template_loaders/errors.py +9 -0
- django_slack_tools/{utils/slack/django.py → slack_messages/validators.py} +1 -1
- django_slack_tools/utils/django/__init__.py +0 -0
- django_slack_tools/utils/import_helper.py +37 -0
- django_slack_tools/utils/repr.py +10 -0
- django_slack_tools/utils/slack/__init__.py +1 -9
- {django_slack_tools-0.2.2.dist-info → django_slack_tools-0.3.1.dist-info}/METADATA +12 -60
- django_slack_tools-0.3.1.dist-info/RECORD +58 -0
- {django_slack_tools-0.2.2.dist-info → django_slack_tools-0.3.1.dist-info}/WHEEL +1 -1
- django_slack_tools/slack_messages/message.py +0 -110
- django_slack_tools/utils/slack/message.py +0 -93
- django_slack_tools/utils/template/__init__.py +0 -5
- django_slack_tools/utils/template/base.py +0 -17
- django_slack_tools/utils/template/dict.py +0 -52
- django_slack_tools-0.2.2.dist-info/RECORD +0 -43
- /django_slack_tools/utils/{model_mixins.py → django/model_mixins.py} +0 -0
- /django_slack_tools/utils/{widgets.py → django/widgets.py} +0 -0
- {django_slack_tools-0.2.2.dist-info → django_slack_tools-0.3.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# noqa: D100
|
|
2
|
+
# flake8: noqa: UP006, UP007, UP035
|
|
3
|
+
# #? Subscription syntax available since Python 3.10
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import uuid
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MessageRequest(BaseModel):
|
|
13
|
+
"""Message request object."""
|
|
14
|
+
|
|
15
|
+
model_config = ConfigDict(extra="forbid")
|
|
16
|
+
|
|
17
|
+
id_: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
18
|
+
channel: Any
|
|
19
|
+
|
|
20
|
+
# Template key is optional to allow lazy initialization of the template key
|
|
21
|
+
template_key: Optional[str]
|
|
22
|
+
|
|
23
|
+
context: Dict[str, Any]
|
|
24
|
+
header: MessageHeader
|
|
25
|
+
|
|
26
|
+
# Also, the body is optional because it is rendered from the template
|
|
27
|
+
body: Optional[MessageBody] = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class MessageHeader(BaseModel): # noqa: D101
|
|
31
|
+
model_config = ConfigDict(extra="forbid")
|
|
32
|
+
|
|
33
|
+
mrkdwn: Optional[str] = None
|
|
34
|
+
parse: Optional[str] = None
|
|
35
|
+
reply_broadcast: Optional[bool] = None
|
|
36
|
+
thread_ts: Optional[str] = None
|
|
37
|
+
unfurl_links: Optional[bool] = None
|
|
38
|
+
unfurl_media: Optional[bool] = None
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_any(cls, obj: MessageHeader | dict[str, Any] | None = None) -> MessageHeader:
|
|
42
|
+
"""Create instance from compatible types."""
|
|
43
|
+
if isinstance(obj, cls):
|
|
44
|
+
return obj
|
|
45
|
+
|
|
46
|
+
if isinstance(obj, dict):
|
|
47
|
+
return cls.model_validate(obj)
|
|
48
|
+
|
|
49
|
+
if obj is None:
|
|
50
|
+
return cls()
|
|
51
|
+
|
|
52
|
+
msg = f"Unsupported type {type(obj)}"
|
|
53
|
+
raise TypeError(msg)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class MessageBody(BaseModel): # noqa: D101
|
|
57
|
+
model_config = ConfigDict(extra="forbid")
|
|
58
|
+
|
|
59
|
+
attachments: Optional[List[dict]] = None
|
|
60
|
+
|
|
61
|
+
# See more about blocks at https://api.slack.com/reference/block-kit/blocks
|
|
62
|
+
blocks: Optional[list[dict]] = None
|
|
63
|
+
|
|
64
|
+
text: Optional[str] = None
|
|
65
|
+
icon_emoji: Optional[str] = None
|
|
66
|
+
icon_url: Optional[str] = None
|
|
67
|
+
metadata: Optional[dict] = None
|
|
68
|
+
username: Optional[str] = None
|
|
69
|
+
|
|
70
|
+
@model_validator(mode="after")
|
|
71
|
+
def _check_at_least_one_field_is_set(self) -> MessageBody:
|
|
72
|
+
if not any((self.attachments, self.blocks, self.text)):
|
|
73
|
+
msg = "At least one of `attachments`, `blocks` and `text` must set"
|
|
74
|
+
raise ValueError(msg)
|
|
75
|
+
|
|
76
|
+
return self
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_any(cls, obj: str | MessageBody | dict[str, Any]) -> MessageBody:
|
|
80
|
+
"""Create instance from compatible types."""
|
|
81
|
+
if isinstance(obj, cls):
|
|
82
|
+
return obj
|
|
83
|
+
|
|
84
|
+
if isinstance(obj, dict):
|
|
85
|
+
return cls.model_validate(obj)
|
|
86
|
+
|
|
87
|
+
if isinstance(obj, str):
|
|
88
|
+
return cls(text=obj)
|
|
89
|
+
|
|
90
|
+
msg = f"Unsupported type {type(obj)}"
|
|
91
|
+
raise TypeError(msg)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# noqa: D100
|
|
2
|
+
# flake8: noqa: UP006, UP007, UP035
|
|
3
|
+
# ? Subscription syntax available since Python 3.10
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Any, Optional
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
from django_slack_tools.slack_messages.request import MessageRequest # noqa: TC001
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MessageResponse(BaseModel):
|
|
14
|
+
"""Response from a messaging backend."""
|
|
15
|
+
|
|
16
|
+
request: Optional[MessageRequest] = None
|
|
17
|
+
ok: bool
|
|
18
|
+
error: Optional[Any] = None
|
|
19
|
+
data: Any
|
|
20
|
+
ts: Optional[str] = None
|
|
21
|
+
parent_ts: Optional[str] = None
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Handy APIs for sending Slack messages."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import TYPE_CHECKING, Any, overload
|
|
7
|
+
|
|
8
|
+
from django_slack_tools.app_settings import get_messenger
|
|
9
|
+
from django_slack_tools.slack_messages.request import MessageBody, MessageHeader, MessageRequest
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from django_slack_tools.slack_messages.response import MessageResponse
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@overload
|
|
18
|
+
def slack_message(
|
|
19
|
+
to: str,
|
|
20
|
+
*,
|
|
21
|
+
messenger_name: str | None = None,
|
|
22
|
+
header: MessageHeader | dict[str, Any] | None = None,
|
|
23
|
+
message: str,
|
|
24
|
+
) -> MessageResponse | None: ... # pragma: no cover
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@overload
|
|
28
|
+
def slack_message(
|
|
29
|
+
to: str,
|
|
30
|
+
*,
|
|
31
|
+
messenger_name: str | None = None,
|
|
32
|
+
header: MessageHeader | dict[str, Any] | None = None,
|
|
33
|
+
template: str | None = None,
|
|
34
|
+
context: dict[str, Any] | None = None,
|
|
35
|
+
) -> MessageResponse | None: ... # pragma: no cover
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def slack_message( # noqa: PLR0913
|
|
39
|
+
to: str,
|
|
40
|
+
*,
|
|
41
|
+
messenger_name: str | None = None,
|
|
42
|
+
header: MessageHeader | dict[str, Any] | None = None,
|
|
43
|
+
template: str | None = None,
|
|
44
|
+
context: dict[str, Any] | None = None,
|
|
45
|
+
message: str | None = None,
|
|
46
|
+
) -> MessageResponse | None:
|
|
47
|
+
"""Shortcut for sending a Slack message.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
to: Recipient.
|
|
51
|
+
messenger_name: Messenger name. If not set, default messenger is used.
|
|
52
|
+
header: Slack message control header.
|
|
53
|
+
template: Message template key. Cannot be used with `message`.
|
|
54
|
+
context: Context for rendering the template. Only used with `template`.
|
|
55
|
+
message: Simple message text. Cannot be used with `template`.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Sent message instance or `None`.
|
|
59
|
+
"""
|
|
60
|
+
if (template and message) or (not template and not message):
|
|
61
|
+
msg = "Either `template` or `message` must be set, but not both."
|
|
62
|
+
raise ValueError(msg)
|
|
63
|
+
|
|
64
|
+
messenger = get_messenger(messenger_name)
|
|
65
|
+
header = MessageHeader.from_any(header)
|
|
66
|
+
|
|
67
|
+
if message:
|
|
68
|
+
request = MessageRequest(
|
|
69
|
+
channel=to,
|
|
70
|
+
header=header,
|
|
71
|
+
body=MessageBody(text=message),
|
|
72
|
+
template_key=None,
|
|
73
|
+
context={},
|
|
74
|
+
)
|
|
75
|
+
return messenger.send_request(request)
|
|
76
|
+
|
|
77
|
+
context = context or {}
|
|
78
|
+
return messenger.send(to, header=header, template=template, context=context)
|
|
@@ -9,7 +9,7 @@ from celery import shared_task
|
|
|
9
9
|
from celery.utils.log import get_task_logger
|
|
10
10
|
from django.utils import timezone
|
|
11
11
|
|
|
12
|
-
from django_slack_tools.slack_messages import
|
|
12
|
+
from django_slack_tools.slack_messages import shortcuts
|
|
13
13
|
from django_slack_tools.slack_messages.models import SlackMessage
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
@@ -19,67 +19,18 @@ logger = get_task_logger(__name__)
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
@shared_task
|
|
22
|
-
def slack_message(
|
|
23
|
-
|
|
24
|
-
*,
|
|
25
|
-
channel: str,
|
|
26
|
-
header: dict[str, Any] | None = None,
|
|
27
|
-
raise_exception: bool = False,
|
|
28
|
-
get_permalink: bool = False,
|
|
29
|
-
) -> int | None:
|
|
30
|
-
"""Celery task wrapper for `message.slack_message`.
|
|
31
|
-
|
|
32
|
-
Args:
|
|
33
|
-
body: Message content, simple message or full request body.
|
|
34
|
-
channel: Channel to send message.
|
|
35
|
-
header: Slack message control header.
|
|
36
|
-
raise_exception: Whether to re-raise caught exception while sending messages.
|
|
37
|
-
get_permalink: Try to get the message permalink via extraneous Slack API calls.
|
|
38
|
-
|
|
39
|
-
Returns:
|
|
40
|
-
ID of sent message.
|
|
41
|
-
"""
|
|
42
|
-
sent_msg = message.slack_message(
|
|
43
|
-
body,
|
|
44
|
-
channel=channel,
|
|
45
|
-
header=header,
|
|
46
|
-
raise_exception=raise_exception,
|
|
47
|
-
get_permalink=get_permalink,
|
|
48
|
-
)
|
|
49
|
-
return sent_msg.id
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
@shared_task
|
|
53
|
-
def slack_message_via_policy( # noqa: PLR0913
|
|
54
|
-
policy: str,
|
|
55
|
-
*,
|
|
56
|
-
header: dict[str, Any] | None = None,
|
|
57
|
-
raise_exception: bool = False,
|
|
58
|
-
lazy: bool = False,
|
|
59
|
-
get_permalink: bool = False,
|
|
60
|
-
context: dict[str, Any] | None = None,
|
|
61
|
-
) -> int:
|
|
62
|
-
"""Celery task wrapper for `message.slack_message_via_policy`.
|
|
22
|
+
def slack_message(*args: Any, **kwargs: Any) -> str | None:
|
|
23
|
+
"""Celery task wrapper for `.shortcuts.slack_message`.
|
|
63
24
|
|
|
64
25
|
Args:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
raise_exception: Whether to re-raise caught exception while sending messages.
|
|
68
|
-
lazy: Decide whether try to create policy with disabled, if not exists.
|
|
69
|
-
get_permalink: Try to get the message permalink via extraneous Slack API calls.
|
|
70
|
-
context: Context variables for message rendering.
|
|
26
|
+
args: Positional arguments.
|
|
27
|
+
kwargs: Keyword arguments.
|
|
71
28
|
|
|
72
29
|
Returns:
|
|
73
|
-
|
|
30
|
+
ID of sent message if any, `None` otherwise.
|
|
74
31
|
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
header=header,
|
|
78
|
-
raise_exception=raise_exception,
|
|
79
|
-
lazy=lazy,
|
|
80
|
-
get_permalink=get_permalink,
|
|
81
|
-
context=context,
|
|
82
|
-
)
|
|
32
|
+
response = shortcuts.slack_message(*args, **kwargs)
|
|
33
|
+
return response.ts if response else None
|
|
83
34
|
|
|
84
35
|
|
|
85
36
|
@shared_task
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .base import BaseTemplateLoader
|
|
2
|
+
from .django import DjangoPolicyTemplateLoader, DjangoTemplateLoader
|
|
3
|
+
from .errors import TemplateLoadError, TemplateNotFoundError
|
|
4
|
+
|
|
5
|
+
__all__ = (
|
|
6
|
+
"BaseTemplateLoader",
|
|
7
|
+
"DjangoPolicyTemplateLoader",
|
|
8
|
+
"DjangoTemplateLoader",
|
|
9
|
+
"TemplateLoadError",
|
|
10
|
+
"TemplateNotFoundError",
|
|
11
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# noqa: D100
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from django_slack_tools.slack_messages.message_templates import BaseTemplate
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseTemplateLoader(ABC):
|
|
12
|
+
"""Base class for template loaders."""
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def load(self, key: str) -> BaseTemplate | None:
|
|
16
|
+
"""Load a template by key."""
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# noqa: D100
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from django.template import TemplateDoesNotExist, engines
|
|
8
|
+
|
|
9
|
+
from django_slack_tools.slack_messages.message_templates import DjangoTemplate, PythonTemplate
|
|
10
|
+
from django_slack_tools.slack_messages.models import SlackMessagingPolicy
|
|
11
|
+
|
|
12
|
+
from .base import BaseTemplateLoader
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from django.template.backends.base import BaseEngine
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DjangoTemplateLoader(BaseTemplateLoader):
|
|
22
|
+
"""Django filesystem-backed template loader."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, *, engine: BaseEngine | None = None) -> None:
|
|
25
|
+
"""Initialize template loader.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
engine: Template engine to use. Defaults to Django engine.
|
|
29
|
+
"""
|
|
30
|
+
self.engine = engines["django"] if engine is None else engine
|
|
31
|
+
|
|
32
|
+
def load(self, key: str) -> DjangoTemplate | None: # noqa: D102
|
|
33
|
+
try:
|
|
34
|
+
return DjangoTemplate(file=key, engine=self.engine)
|
|
35
|
+
except TemplateDoesNotExist:
|
|
36
|
+
logger.debug("Template not found: %s", key)
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class DjangoPolicyTemplateLoader(BaseTemplateLoader):
|
|
41
|
+
"""Django database-backed template loader."""
|
|
42
|
+
|
|
43
|
+
def load(self, key: str) -> PythonTemplate | DjangoTemplate | None: # noqa: D102
|
|
44
|
+
return self._get_template_from_policy(policy_or_code=key)
|
|
45
|
+
|
|
46
|
+
def _get_template_from_policy(
|
|
47
|
+
self,
|
|
48
|
+
policy_or_code: SlackMessagingPolicy | str,
|
|
49
|
+
) -> PythonTemplate | DjangoTemplate | None:
|
|
50
|
+
"""Get template instance."""
|
|
51
|
+
if isinstance(policy_or_code, str):
|
|
52
|
+
try:
|
|
53
|
+
policy = SlackMessagingPolicy.objects.get(code=policy_or_code)
|
|
54
|
+
except SlackMessagingPolicy.DoesNotExist:
|
|
55
|
+
logger.warning("Policy not found: %s", policy_or_code)
|
|
56
|
+
return None
|
|
57
|
+
else:
|
|
58
|
+
policy = policy_or_code
|
|
59
|
+
|
|
60
|
+
if policy.template_type == SlackMessagingPolicy.TemplateType.PYTHON:
|
|
61
|
+
return PythonTemplate(policy.template)
|
|
62
|
+
|
|
63
|
+
if policy.template_type == SlackMessagingPolicy.TemplateType.DJANGO:
|
|
64
|
+
try:
|
|
65
|
+
return DjangoTemplate(file=policy.template)
|
|
66
|
+
except TemplateDoesNotExist:
|
|
67
|
+
logger.debug("Template not found: %s", policy.template)
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
if policy.template_type == SlackMessagingPolicy.TemplateType.DJANGO_INLINE:
|
|
71
|
+
return DjangoTemplate(inline=policy.template)
|
|
72
|
+
|
|
73
|
+
msg = f"Unsupported template type: {policy.template_type!r}"
|
|
74
|
+
raise ValueError(msg)
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from django.core.exceptions import ValidationError
|
|
6
6
|
|
|
7
|
-
from .
|
|
7
|
+
from django_slack_tools.slack_messages.request import MessageBody, MessageHeader
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def header_validator(d: dict) -> None:
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Application settings."""
|
|
2
|
+
# flake8: noqa: UP007
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Any, TypedDict, Union
|
|
7
|
+
|
|
8
|
+
from django.utils.module_loading import import_string
|
|
9
|
+
from typing_extensions import NotRequired
|
|
10
|
+
|
|
11
|
+
LazyInitSpec = TypedDict(
|
|
12
|
+
"LazyInitSpec",
|
|
13
|
+
{
|
|
14
|
+
"class": str,
|
|
15
|
+
"args": NotRequired[tuple],
|
|
16
|
+
"kwargs": NotRequired[dict[str, Any]],
|
|
17
|
+
},
|
|
18
|
+
)
|
|
19
|
+
LazyInitSupported = Union[LazyInitSpec, str]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def lazy_init(spec: LazyInitSupported) -> Any:
|
|
23
|
+
"""Initialize an object from a lazy init spec.
|
|
24
|
+
|
|
25
|
+
If string is passed, it is treated as a class import path.
|
|
26
|
+
Otherwise, it is expected to be a dictionary with keys:
|
|
27
|
+
|
|
28
|
+
- `class`: class import path,
|
|
29
|
+
- `args`: positional arguments,
|
|
30
|
+
- `kwargs`: keyword arguments.
|
|
31
|
+
"""
|
|
32
|
+
if isinstance(spec, str):
|
|
33
|
+
spec = LazyInitSpec({"class": spec})
|
|
34
|
+
|
|
35
|
+
class_ = import_string(spec["class"])
|
|
36
|
+
args, kwargs = spec.get("args", ()), spec.get("kwargs", {})
|
|
37
|
+
return class_(*args, **kwargs)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# noqa: D100
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def make_repr(obj: Any) -> str:
|
|
8
|
+
"""Make a repr string for an object."""
|
|
9
|
+
args = ", ".join(f"{k}={v!r}" for k, v in obj.__dict__.items())
|
|
10
|
+
return f"{obj.__class__.__name__}({args})"
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
from .django import body_validator, header_validator
|
|
2
|
-
from .message import MessageBody, MessageHeader
|
|
3
1
|
from .misc import get_block_kit_builder_url
|
|
4
2
|
|
|
5
|
-
__all__ = (
|
|
6
|
-
"body_validator",
|
|
7
|
-
"header_validator",
|
|
8
|
-
"MessageBody",
|
|
9
|
-
"MessageHeader",
|
|
10
|
-
"get_block_kit_builder_url",
|
|
11
|
-
)
|
|
3
|
+
__all__ = ("get_block_kit_builder_url",)
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: django-slack-tools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Little helpers working with Slack bot 🤖 in Django.
|
|
5
5
|
Author-email: Yuchan Lee <lasuillard@gmail.com>
|
|
6
|
-
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: <4.0,>=3.9
|
|
7
9
|
Requires-Dist: django<5.2,>=4.2
|
|
10
|
+
Requires-Dist: pydantic<3,>=2
|
|
8
11
|
Requires-Dist: slack-bolt<2,>=1
|
|
9
12
|
Requires-Dist: xmltodict<1,>=0.14.1
|
|
10
13
|
Provides-Extra: celery
|
|
11
14
|
Requires-Dist: celery<6,>=5; extra == 'celery'
|
|
12
15
|
Provides-Extra: dev
|
|
13
|
-
Requires-Dist: django-debug-toolbar
|
|
16
|
+
Requires-Dist: django-debug-toolbar<6.0,>=4.4; extra == 'dev'
|
|
14
17
|
Requires-Dist: django-extensions~=3.2; extra == 'dev'
|
|
15
18
|
Requires-Dist: django-stubs[compatible-mypy]~=5.1; extra == 'dev'
|
|
16
19
|
Requires-Dist: ipykernel~=6.27; extra == 'dev'
|
|
@@ -23,8 +26,9 @@ Provides-Extra: test
|
|
|
23
26
|
Requires-Dist: coverage~=7.3; extra == 'test'
|
|
24
27
|
Requires-Dist: django-coverage-plugin~=3.1; extra == 'test'
|
|
25
28
|
Requires-Dist: factory-boy~=3.3; extra == 'test'
|
|
26
|
-
Requires-Dist: faker
|
|
27
|
-
Requires-Dist:
|
|
29
|
+
Requires-Dist: faker<37.0,>=30.3; extra == 'test'
|
|
30
|
+
Requires-Dist: nox<2025.3.0,>=2024.10.9; extra == 'test'
|
|
31
|
+
Requires-Dist: pytest-cov<7,>=5; extra == 'test'
|
|
28
32
|
Requires-Dist: pytest-django~=4.9; extra == 'test'
|
|
29
33
|
Requires-Dist: pytest-sugar~=1.0; extra == 'test'
|
|
30
34
|
Requires-Dist: pytest-xdist~=3.6; extra == 'test'
|
|
@@ -66,61 +70,9 @@ And more in future roadmap...
|
|
|
66
70
|
|
|
67
71
|
Currently it is focused on messaging features. In future, hoping to bring more helpful features across Slack Bot ecosystem, such as event subscriptions, modals, bot interactions, etc.
|
|
68
72
|
|
|
69
|
-
##
|
|
73
|
+
## 🗞️ Documentation
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
> [!WARNING]
|
|
74
|
-
> 0.x versions are for development. Breaking changes can be made at any time. If gonna use this package, recommend to pin down the version.
|
|
75
|
-
|
|
76
|
-
Install the package:
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
$ pip install django-slack-tools
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Add the app to the your Django settings:
|
|
83
|
-
|
|
84
|
-
```python
|
|
85
|
-
INSTALLED_APPS = [
|
|
86
|
-
...
|
|
87
|
-
"django.contrib.messages", # Used in admin
|
|
88
|
-
"django_slack_tools.slack_messages",
|
|
89
|
-
...
|
|
90
|
-
]
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Add configuration for application:
|
|
94
|
-
|
|
95
|
-
```python
|
|
96
|
-
DJANGO_SLACK_TOOLS = {
|
|
97
|
-
# Module path to Slack Bolt application or callable returns the app
|
|
98
|
-
"SLACK_APP": "path.to.your.slack.app",
|
|
99
|
-
|
|
100
|
-
# Messaging backend configuration
|
|
101
|
-
"BACKEND": {
|
|
102
|
-
"NAME": "django_slack_tools.slack_messages.backends.SlackBackend",
|
|
103
|
-
"OPTIONS": {
|
|
104
|
-
# TODO(#44): Reasonable defaults to reduce some duplicates
|
|
105
|
-
"slack_app": "path.to.your.slack.app",
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
Then, run the database migration and send messages:
|
|
112
|
-
|
|
113
|
-
```python
|
|
114
|
-
from django_slack_tools.slack_messages.message import slack_message
|
|
115
|
-
|
|
116
|
-
message = slack_message(
|
|
117
|
-
"I like threading",
|
|
118
|
-
channel="id-of-channel",
|
|
119
|
-
header={"reply_broadcast": True},
|
|
120
|
-
)
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Please check the [documentation](https://lasuillard.github.io/django-slack-tools/) for more about details.
|
|
75
|
+
Please check the [documentation](https://lasuillard.github.io/django-slack-tools/) for more details.
|
|
124
76
|
|
|
125
77
|
## 💖 Contributing
|
|
126
78
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
django_slack_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
django_slack_tools/app_settings.py,sha256=4tM-5vN7XosoSinGfm9wwdm1anN1AAAXlhLYAVUdU48,2858
|
|
3
|
+
django_slack_tools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
django_slack_tools/views.py,sha256=XuQh0g9A2O51DVkMUhm8x8cswZytZcECNIsDuLsM0UI,1168
|
|
5
|
+
django_slack_tools/locale/ko_KR/LC_MESSAGES/django.po,sha256=o-05SqnUYK7mS5PWO1mFhWEdbPRCm00sue81itdhvco,13813
|
|
6
|
+
django_slack_tools/slack_messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
django_slack_tools/slack_messages/apps.py,sha256=nLVxuLE-1VIqkcVYEdtlg6ffD3blonGCMKaTd_F5jis,610
|
|
8
|
+
django_slack_tools/slack_messages/messenger.py,sha256=-H7HjfPl8xzQqaW0bqbr8pRhdrLr981eXNPMbILyShQ,6284
|
|
9
|
+
django_slack_tools/slack_messages/request.py,sha256=nDp9oV-rbAKI3nvGNvWc2DWhPE3R3uVKZVwPLwQaM4Q,2643
|
|
10
|
+
django_slack_tools/slack_messages/response.py,sha256=lRW4u07RG_pYghFkmXqiAzHCHic20DvY3nPqOQ1Seas,538
|
|
11
|
+
django_slack_tools/slack_messages/shortcuts.py,sha256=LB-v0GkgByYR0MwH2DqlFPQ_nNCBMMJxa_10HKkdBb4,2337
|
|
12
|
+
django_slack_tools/slack_messages/tasks.py,sha256=I99FYyYP1IpgWUgcIcWFiO47NQYDfV5LYm8NxUUTLec,1823
|
|
13
|
+
django_slack_tools/slack_messages/validators.py,sha256=UpH53b78AgMfk8FKB3eX0tbtrCnLuhF4SI_PS3jfn9Q,776
|
|
14
|
+
django_slack_tools/slack_messages/admin/__init__.py,sha256=FEBZlgze8Nf5wn8ixm9FcsL3na2WYykmm2U-iJXh_mQ,305
|
|
15
|
+
django_slack_tools/slack_messages/admin/mention.py,sha256=Z3Cx-6WKECsbXnyby1qCEMllvxJEIn_TdwexsjEaiIo,4399
|
|
16
|
+
django_slack_tools/slack_messages/admin/message.py,sha256=lYEc67RVRCyak-DMDnOdfh1ZJPrwVSSl7zwiIzqVz2Q,2248
|
|
17
|
+
django_slack_tools/slack_messages/admin/message_recipient.py,sha256=GxzXDG0oY5wp8TTm16LfHsJdvtcbLJm1mPAMD_6UKo8,4039
|
|
18
|
+
django_slack_tools/slack_messages/admin/messaging_policy.py,sha256=DQRBeDPVqjC2f-Tq7g4c18ZuOW1-ViNWuPCkyfFAQPs,4621
|
|
19
|
+
django_slack_tools/slack_messages/backends/__init__.py,sha256=K7xJDVatvksJf7UoPcK3-UO-h5XzE-HJ-nYTihIW6ZM,277
|
|
20
|
+
django_slack_tools/slack_messages/backends/base.py,sha256=8GgmOGSZYoMwQanhintxMCkWgYsyZhpN0aK1J8j1hVo,1877
|
|
21
|
+
django_slack_tools/slack_messages/backends/dummy.py,sha256=Cl6ktqpe4Yoj_rhvVxEmmRc8AmCiDuU9Eprw2Moi_j0,681
|
|
22
|
+
django_slack_tools/slack_messages/backends/logging_.py,sha256=PEIHs34-l-CP8sy5Zp3NHAhUe3nEuYpsxMr4TcjheoM,609
|
|
23
|
+
django_slack_tools/slack_messages/backends/slack.py,sha256=s17w2F2K5UA15qKlyoHObQi_rQZmdX2sWFFkmjNDq6o,2907
|
|
24
|
+
django_slack_tools/slack_messages/message_templates/__init__.py,sha256=KXtUOejEBi9hR9Gn9MwXHIclTmgGsMEHx4vibsbc1uM,165
|
|
25
|
+
django_slack_tools/slack_messages/message_templates/base.py,sha256=p2-JM4QgDTyGlEd6gpJqjTU5lpnPFEJwbVkzJwiELpo,373
|
|
26
|
+
django_slack_tools/slack_messages/message_templates/django.py,sha256=1_MU5ExyRv7t-CgkrGpJ39OQF7Wp9IkVdnBX-ETwsm8,4326
|
|
27
|
+
django_slack_tools/slack_messages/message_templates/python.py,sha256=qFWcWJLDbs9lK42_F3-fbkH7mxwDC3TgVYtjEaZvyI0,1113
|
|
28
|
+
django_slack_tools/slack_messages/middlewares/__init__.py,sha256=Y1iVV37JWjQZqL5vzbVGJkhtyQFDKrZL_YSj1UHiyXs,194
|
|
29
|
+
django_slack_tools/slack_messages/middlewares/base.py,sha256=8s3I1e_jjDKJDgKenaKy32DwkqfiZeu5uJKpIWXRiD8,974
|
|
30
|
+
django_slack_tools/slack_messages/middlewares/django.py,sha256=ZCkfqlIzqai6TFOHcYuy9Hl_xO0s5OrZgXSVQ-umFXc,9290
|
|
31
|
+
django_slack_tools/slack_messages/migrations/0001_initial.py,sha256=NvsXJoPfG3UNEIxzioMiyjVsv0B26UDTouznZr7a_C0,11622
|
|
32
|
+
django_slack_tools/slack_messages/migrations/0002_default_policy.py,sha256=a1pf6bGhFszOz2awUudQnm9FyxIdWcs3ILsWCADG4YU,1145
|
|
33
|
+
django_slack_tools/slack_messages/migrations/0003_default_recipient.py,sha256=SqqawYxEHItmCYAgfv-EAsnc7Fxg3mJtKw6ccDCF1Eo,1153
|
|
34
|
+
django_slack_tools/slack_messages/migrations/0004_slackmessagingpolicy_template_type.py,sha256=u5WkhdPxzXLraw8oTvC0sBV5GUFR0fDghtoc2RvTFQU,679
|
|
35
|
+
django_slack_tools/slack_messages/migrations/0005_alter_slackmessagingpolicy_template_type.py,sha256=guAG77_8eDXMECE6pZKkmjrfvWflmhmVuFirTLVb3KU,1652
|
|
36
|
+
django_slack_tools/slack_messages/migrations/0006_alter_slackmessage_id.py,sha256=dG97L44IQRjXJ7MFH0SPzGGhV_NcaOLUemh9yiY3siI,626
|
|
37
|
+
django_slack_tools/slack_messages/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
django_slack_tools/slack_messages/models/__init__.py,sha256=5niO9vU-YMGRCt7wQMXbN-hSrLpJLtukMKjjv-aFeaQ,265
|
|
39
|
+
django_slack_tools/slack_messages/models/mention.py,sha256=ofLH4q8pzleW3ReCnD83BOOzWopNUsD0MjKkz8s7HYI,2084
|
|
40
|
+
django_slack_tools/slack_messages/models/message.py,sha256=iTlRGsHM0SQ1853uODqJSt6DKIdw7HFScmUSZZJeVRs,3787
|
|
41
|
+
django_slack_tools/slack_messages/models/message_recipient.py,sha256=rqIWfhqTQrjejRXNz2TT5JBi89zWlngaSiQ81anQCAA,1673
|
|
42
|
+
django_slack_tools/slack_messages/models/messaging_policy.py,sha256=FFndVT-hOJDBMCvQ8to6VARlqlpPyXPX8UKNQaVO2k0,2975
|
|
43
|
+
django_slack_tools/slack_messages/template_loaders/__init__.py,sha256=0RKD-AYYplt0KbkgGwEwQFj-rpfArxXLkt3fiXl-iU0,324
|
|
44
|
+
django_slack_tools/slack_messages/template_loaders/base.py,sha256=2I7i9UIoEk1gXCKoGFbHsOYKCKI6IBr6k_LzL1c1-mY,406
|
|
45
|
+
django_slack_tools/slack_messages/template_loaders/django.py,sha256=TCKyypEe17uikg0wLiiRqYO_TxvKyGpQpNYmJhFwoD4,2580
|
|
46
|
+
django_slack_tools/slack_messages/template_loaders/errors.py,sha256=qFw6FrObL1nAW-fE07N_leygvE1ZCEhLMiLGLMSGtG8,186
|
|
47
|
+
django_slack_tools/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
+
django_slack_tools/utils/import_helper.py,sha256=o3BhGYzPwXPNWz1bDWyTbAp0R5bSpdewgmmiob6rgIw,992
|
|
49
|
+
django_slack_tools/utils/repr.py,sha256=BVmm5vWb8Kws5jJygX1_VeZmjfH6IphTFVXqJMM2r7A,265
|
|
50
|
+
django_slack_tools/utils/django/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
|
+
django_slack_tools/utils/django/model_mixins.py,sha256=bNPx3yudzUku8TnjstestOs6YEww58FmBUypL5caYdI,634
|
|
52
|
+
django_slack_tools/utils/django/widgets.py,sha256=v1EorzDSDXn_ZZgBHVFYMhWPG5RsfEI3L3VFf282svg,876
|
|
53
|
+
django_slack_tools/utils/slack/__init__.py,sha256=rByQXL5NEZPRVVv7jdh18hVh2OBxnlym3reDKSTWYLE,86
|
|
54
|
+
django_slack_tools/utils/slack/misc.py,sha256=bl7xp43IsTt0FUZWfGH9kk4qYMU0UjIhuGs35zkLyyE,1099
|
|
55
|
+
django_slack_tools-0.3.1.dist-info/METADATA,sha256=N7Izcd6A_vZmJFnVcOhoy0t91h0MhbFbw3caxD53N1A,3354
|
|
56
|
+
django_slack_tools-0.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
57
|
+
django_slack_tools-0.3.1.dist-info/licenses/LICENSE,sha256=lPgIV-vu4gapOeftp1Dp5RsN6zlsiMiubqCNeP466hw,1067
|
|
58
|
+
django_slack_tools-0.3.1.dist-info/RECORD,,
|