Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bringing test branch up to date to main #560

Merged
merged 32 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e0bac4e
Build(deps-dev): bump pytest from 8.0.1 to 8.0.2
dependabot[bot] Feb 26, 2024
7b5c6f1
Build(deps-dev): bump pytype from 2024.2.13 to 2024.2.27
dependabot[bot] Mar 4, 2024
9cc55c5
Configurable log level
Roy-Orbison Mar 12, 2024
f62a50e
Reorder imports as per isort rules
unode Mar 15, 2024
be718ad
Reorder imports as per isort rules
unode Mar 15, 2024
b3276a9
Build(deps-dev): bump filelock from 3.13.1 to 3.13.3 (#506)
dependabot[bot] Mar 26, 2024
f327c2d
CI: update pre-commit hooks (#501)
github-actions[bot] Mar 26, 2024
8207b1f
Build(deps-dev): bump pytype from 2024.2.27 to 2024.3.19 (#505)
dependabot[bot] Mar 26, 2024
39dba7c
Build(deps-dev): bump black from 24.2.0 to 24.3.0 (#503)
dependabot[bot] Mar 26, 2024
e01ceff
Build(deps-dev): bump pytest from 8.0.2 to 8.1.1 (#499)
dependabot[bot] Mar 26, 2024
a585469
Add ruff to pre-commit framework
unode Mar 31, 2024
5e2bd43
Fix ruff failures
unode Mar 31, 2024
a91a1eb
CI: update pre-commit hooks (#508)
github-actions[bot] Apr 2, 2024
13c28da
Build(deps-dev): bump filelock from 3.13.3 to 3.15.4
dependabot[bot] Jun 24, 2024
7571715
Build(deps-dev): bump black from 24.3.0 to 24.4.2
dependabot[bot] Apr 29, 2024
b8342b0
Build(deps-dev): bump pytest-xdist from 3.5.0 to 3.6.1
dependabot[bot] Apr 29, 2024
9ac4063
CI: update pre-commit hooks
unode Jul 6, 2024
426c82b
Build(deps-dev): bump pytype from 2024.3.19 to 2024.4.11
dependabot[bot] Jul 14, 2024
f9a2ca6
Build(deps-dev): bump flake8 from 7.0.0 to 7.1.0
dependabot[bot] Jul 14, 2024
c4d7575
Build(deps-dev): bump pytest from 8.1.1 to 8.2.2
dependabot[bot] Jul 14, 2024
4d78f22
CI: update pre-commit hooks
unode Jul 19, 2024
d87a4e3
remove deprecated `asyncio.iscoroutinefunction` and use `inspect.isco…
ArtemIsmagilov Jul 16, 2024
cdcedb3
update to latest spec compose
ArtemIsmagilov Jul 15, 2024
aab2de4
correction initialize args and comprehention constructions (#527)
ArtemIsmagilov Jul 26, 2024
d3e0d5e
CI: update pre-commit hooks
unode Jul 27, 2024
6a94cbd
try up deps (#536)
ArtemIsmagilov Dec 5, 2024
b1e36ac
remove dual list convert (#534)
ArtemIsmagilov Dec 5, 2024
2866624
not needed convert args to -> tuple -> list (#532)
ArtemIsmagilov Dec 5, 2024
b64b972
optimization for loops and refac to py3 (#528)
ArtemIsmagilov Dec 5, 2024
164ba5e
CI: update pre-commit hooks (#540)
github-actions[bot] Dec 5, 2024
4117196
Update mattermostautodriver requirement from ~=1.3.0 to ~=2.0.0 (#464)
dependabot[bot] Dec 10, 2024
04bf419
Added file_ids handling for incoming messages (#558)
Sergeydmitr Dec 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ jobs:
run: pip install -e .[dev]
- name: Launch test server
working-directory: tests/integration_tests
run: docker-compose up -d && sleep 30
- name: Print docker info
run: docker ps -a
run: docker compose up -d && sleep 30
- name: Print docker services info
run: docker compose ps -a
- name: Run integration tests
working-directory: tests/integration_tests
run: pytest . -vv -n auto
14 changes: 10 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: end-of-file-fixer
- id: fix-byte-order-marker
Expand All @@ -22,16 +22,22 @@ repos:
- "--filter-files"
- repo: https://github.com/psf/black
# Code style formatting
rev: 24.2.0
rev: 24.8.0
hooks:
- id: black
exclude: (.*/)*snapshots/
- repo: https://github.com/PyCQA/flake8
# Checks the code for PEP8 violations and common pitfals
rev: 7.0.0
rev: 7.1.1
hooks:
- id: flake8
exclude: (.*/)*snapshots/
# Ruff is a "fastar than flake8" linter/formatter
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
# Run the linter.
- id: ruff
- repo: https://github.com/mattseymour/pre-commit-pytype
rev: '2023.5.8'
hooks:
Expand All @@ -54,6 +60,6 @@ repos:
- "88"
- repo: https://github.com/pycqa/doc8
# sphinx rst style checker
rev: v1.1.1
rev: v1.1.2
hooks:
- id: doc8
2 changes: 0 additions & 2 deletions docker-compose.yml → compose.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.7"

services:
app:
# This docker-compose file specifies a mmpy_bot
Expand Down
12 changes: 6 additions & 6 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
black==24.2.0
black==24.4.2
docformatter==1.7.5
filelock==3.13.1
filelock==3.15.4
isort==5.13.2
flake8==7.0.0
pytest==8.0.1
pytest-xdist==3.5.0
pytype==2024.2.13
flake8==7.1.0
pytest==8.2.2
pytest-xdist==3.6.1
pytype==2024.4.11
snapshottest==0.6.0
4 changes: 3 additions & 1 deletion mmpy_bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ def _register_logger(self):
**{
"format": self.settings.LOG_FORMAT,
"datefmt": self.settings.LOG_DATE_FORMAT,
"level": logging.DEBUG if self.settings.DEBUG else logging.INFO,
"level": (
logging.DEBUG if self.settings.DEBUG else self.settings.LOG_LEVEL
),
"filename": self.settings.LOG_FILE,
"filemode": "w",
}
Expand Down
45 changes: 22 additions & 23 deletions mmpy_bot/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def create_post(
message: str,
file_paths: Optional[Sequence[str]] = None,
root_id: str = "",
props: Dict = {},
props: Optional[Dict] = None,
ephemeral_user_id: Optional[str] = None,
):
"""Create a post in the specified channel with the specified text.
Expand All @@ -52,8 +52,8 @@ def create_post(
paths are specified, those files will be uploaded to mattermost first and then
attached.
"""
if file_paths is None:
file_paths = []
file_paths = file_paths or []
props = props or {}

file_ids = (
self.upload_files(file_paths, channel_id) if len(file_paths) > 0 else []
Expand Down Expand Up @@ -88,9 +88,9 @@ def get_post_thread(self, post_id: str):
duplicate and wrongly ordered entries in the ordered list."""
thread_info = self.posts.get_post_thread(post_id)

id_stamps = []
for id, post in thread_info["posts"].items():
id_stamps.append((id, int(post["create_at"])))
id_stamps = (
(id, int(post["create_at"])) for id, post in thread_info["posts"].items()
)
# Sort the posts by their timestamps
sorted_stamps = sorted(id_stamps, key=lambda x: x[-1])
# Overwrite the order with the sorted list
Expand All @@ -116,7 +116,7 @@ def reply_to(
message: Message,
response: str,
file_paths: Optional[Sequence[str]] = None,
props: Dict = {},
props: Optional[Dict] = None,
ephemeral: bool = False,
direct: bool = False,
):
Expand All @@ -127,8 +127,8 @@ def reply_to(

Also supports replying privately by setting direct=True.
"""
if file_paths is None:
file_paths = []
file_paths = file_paths or []
props = props or {}

if direct and not message.is_direct_message:
# NOTE we explicitly don't pass root_id as it would refer to a
Expand Down Expand Up @@ -160,14 +160,15 @@ def direct_message(
message: str,
file_paths: Optional[Sequence[str]] = None,
root_id: str = "",
props: Dict = {},
props: Optional[Dict] = None,
ephemeral_user_id: Optional[str] = None,
):
# Private/direct messages are sent to a special channel that
# includes the bot and the recipient
direct_id = self.channels.create_direct_channel([self.user_id, receiver_id])[
"id"
]
props = props or {}

return self.create_post(
channel_id=direct_id,
Expand Down Expand Up @@ -199,22 +200,20 @@ def upload_files(
) -> List[str]:
"""Given a list of file paths and the channel id, uploads the corresponding
files and returns a list their internal file IDs."""
file_list = []
for path in file_paths:
path = Path(path)
# Note: 'files' should be a name of an expected attribute in the body
# but seems to be ignored when simply uploading files to mattermost
file_list.append(
# Note: 'files' should be a name of an expected attribute in the body
# but seems to be ignored when simply uploading files to mattermost
file_list = [
(
"files",
(
"files",
(
path.name,
Path(path).read_bytes(),
),
)
Path(path).name,
Path(path).read_bytes(),
),
)
for path in file_paths
]

result = self.files.upload_file(
files=file_list, data={"channel_id": channel_id}
)
return list(info["id"] for info in result["file_infos"])
return [info["id"] for info in result["file_infos"]]
53 changes: 24 additions & 29 deletions mmpy_bot/event_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
log = logging.getLogger("mmpy.event_handler")


class EventHandler(object):
class EventHandler:
def __init__(
self,
driver: Driver,
Expand All @@ -37,10 +37,8 @@ def start(self):
def _should_ignore(self, message: Message):
# Ignore message from senders specified in settings, and maybe from ourself
return (
True
if message.sender_name.lower()
message.sender_name.lower()
in (name.lower() for name in self.settings.IGNORE_USERS)
else False
) or (self.ignore_own_messages and message.sender_name == self.driver.username)

async def _check_queue_loop(self, webhook_queue: queue.Queue):
Expand Down Expand Up @@ -75,37 +73,34 @@ async def _handle_post(self, post):

# Find all the listeners that match this message, and have their plugins handle
# the rest.
tasks = []
for matcher, functions in self.plugin_manager.message_listeners.items():
match = matcher.search(message.text)
if match:
groups = list([group for group in match.groups() if group != ""])
for function in functions:
# Create an asyncio task to handle this callback
tasks.append(
asyncio.create_task(
function.plugin.call_function(
function, message, groups=groups
)
)
)
tasks = [
asyncio.create_task(
function.plugin.call_function(
function,
message,
groups=[group for group in match.groups() if group != ""],
)
)
for matcher, functions in self.plugin_manager.message_listeners.items()
if (match := matcher.search(message.text))
for function in functions
]

# Execute the callbacks in parallel
asyncio.gather(*tasks)

async def _handle_webhook(self, event: WebHookEvent):
# Find all the listeners that match this webhook id, and have their plugins
# handle the rest.
tasks = []
for matcher, functions in self.plugin_manager.webhook_listeners.items():
match = matcher.search(event.webhook_id)
if match:
for function in functions:
# Create an asyncio task to handle this callback
tasks.append(
asyncio.create_task(
function.plugin.call_function(function, event)
)
)

tasks = [
# Create an asyncio task to handle this callback
asyncio.create_task(function.plugin.call_function(function, event))
for matcher, functions in self.plugin_manager.webhook_listeners.items()
if matcher.search(event.webhook_id)
for function in functions
]

# If this webhook doesn't correspond to any listeners, signal the WebHookServer
# to not wait for any response
if len(tasks) == 0:
Expand Down
8 changes: 4 additions & 4 deletions mmpy_bot/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(
)

self.function = function
self.is_coroutine = asyncio.iscoroutinefunction(function)
self.is_coroutine = inspect.iscoroutinefunction(function)
self.is_click_function: bool = False
self.matcher = matcher
self.metadata = metadata
Expand Down Expand Up @@ -100,7 +100,7 @@ def __init__(

if self.is_click_function:
_function = self.function.callback
if asyncio.iscoroutinefunction(_function):
if inspect.iscoroutinefunction(_function):
raise ValueError(
"Combining click functions and coroutines is currently not supported!"
" Consider using a regular function, which will be threaded by default."
Expand Down Expand Up @@ -155,10 +155,10 @@ def __call__(self, message: Message, *args):
assert len(args) <= 1 # There is only one group, (.*)?
if len(args) == 1:
# Turn space-separated string into list
args = tuple(shlex.split(args[0]))
args = shlex.split(args[0])
try:
ctx = self.function.make_context(
info_name=self.plugin.__class__.__name__, args=list(args)
info_name=self.plugin.__class__.__name__, args=args
)
ctx.params.update({"self": self.plugin, "message": message})
return self.function.invoke(ctx)
Expand Down
13 changes: 10 additions & 3 deletions mmpy_bot/settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import collections
import logging
import os
import warnings
from dataclasses import dataclass, field, fields
Expand All @@ -8,9 +9,7 @@
def _get_comma_separated_list(string: str, type=str):
values = string.split(",")
# Convert to the specified type if necessary.
if type is not str:
values = list([type(value) for value in values])
return values
return values if type is str else [type(value) for value in values]


def _is_valid_option(_type, valid_types):
Expand Down Expand Up @@ -59,6 +58,7 @@ class Settings:
DEBUG: bool = False
# Respond to channel message "!help" (without @bot)
RESPOND_CHANNEL_HELP: bool = False
LOG_LEVEL: int = logging.INFO
LOG_FILE: Optional[str] = None
LOG_FORMAT: str = "[%(asctime)s][%(name)s][%(levelname)s] %(message)s"
LOG_DATE_FORMAT: str = "%m/%d/%Y %H:%M:%S"
Expand Down Expand Up @@ -91,6 +91,13 @@ def __post_init__(self):
)
self.MATTERMOST_API_PATH = self.MATTERMOST_API_PATH[: -len(api_url)]

if self.DEBUG:
warnings.warn(
"DEBUG has been deprecated and will be removed in a future release. "
"Set LOG_LEVEL to logging.DEBUG to increase verbosity.",
DeprecationWarning,
)

def _check_environment_variables(self):
for f in fields(self):
if f.name in os.environ:
Expand Down
4 changes: 4 additions & 0 deletions mmpy_bot/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ def channel_id(self):
def channel_name(self):
return self.body["data"]["channel_name"]

@cached_property
def file_ids(self):
return self.body["data"]["post"].get("file_ids", [])

@cached_property
def is_direct_message(self):
return self.body["data"]["channel_type"] == "D"
Expand Down
9 changes: 5 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
aiohttp>=3.7.4.post0
click>=7.0
mattermostautodriver~=1.3.0
schedule>=0.6.0
httpx>=0.27.0,<0.28.0
aiohttp>=3.9.5
click>=8.1.7
mattermostautodriver>=2.0.0
schedule>=1.2.2
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.7"

services:
app:
container_name: "mattermost-bot-test"
Expand Down
2 changes: 1 addition & 1 deletion tests/integration_tests/test_direct_message_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import time
from string import ascii_letters

from .utils import start_bot # noqa, only imported so that the bot is started
from .utils import start_bot # noqa: F401, only imported so that the bot is started
from .utils import MAIN_BOT_ID, OFF_TOPIC_ID, RESPONSE_TIMEOUT, TEAM_ID
from .utils import driver as driver_fixture
from .utils import expect_reply
Expand Down
2 changes: 1 addition & 1 deletion tests/integration_tests/test_example_plugin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import time

from .utils import start_bot # noqa, only imported so that the bot is started
from .utils import start_bot # noqa: F401, only imported so that the bot is started
from .utils import MAIN_BOT_ID, OFF_TOPIC_ID, RESPONSE_TIMEOUT, TEAM_ID
from .utils import driver as driver_fixture
from .utils import expect_reply
Expand Down
2 changes: 1 addition & 1 deletion tests/integration_tests/test_webhook_example.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import asyncio
import time

from .utils import start_bot # noqa, only imported so that the bot is started
from .utils import start_bot # noqa: F401, only imported so that the bot is started
from .utils import OFF_TOPIC_ID, RESPONSE_TIMEOUT
from .utils import driver as driver_fixture
from .utils import expect_reply
Expand Down
Loading
Loading