Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ Alternatively, install all required development environment dependencies, includ
make install
```

For developement and testing it is sometimes usefull to trigger TGTG Magic Bag events.
For developement and testing it is sometimes useful to trigger TGTG Magic Bag events.
For this purpose you can run the TGTG dev API proxy server.
The proxy redirects all requests to the official TGTG API server.
The responses from the item endpoint are modified by randomizing the amount of available magic bags.
Expand All @@ -245,7 +245,7 @@ make server

Feel free to create and contribute new notifiers for other services and endpoints.
You can use an existing notifier as a template or build upon the webhook notifier.
E.g. see the [ifttt notifier](https://github.com/Der-Henning/tgtg/blob/main/src/notifiers/ifttt.py).
E.g. see the [ifttt notifier](https://github.com/Der-Henning/tgtg/blob/main/tgtg_scanner/notifiers/ifttt.py).

---
If you want to support me, feel free to buy me a coffee.
Expand Down
9 changes: 9 additions & 0 deletions config.sample.ini
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,12 @@ URL =
Enabled = false
Command =
; Cron =

[DISCORD]
## Register an application and associated bot user for use with TGTG scanner at https://discord.com/developers/applications
## See wiki for more information
Enabled = false
Prefix = !
Token =
Body =
; Cron =
765 changes: 614 additions & 151 deletions poetry.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ version = "1.20.3"
apprise = "^1.4.0"
colorlog = "^6.7.0"
cron-descriptor = "^1.4.0"
discord = "^2.3.2"
googlemaps = "^4.10.0"
humanize = "^4.7.0"
packaging = "^23.1"
packaging = "^24.0"
progress = "^1.6"
prometheus-client = "^0.20.0"
pycron = "^3.0.0"
Expand All @@ -35,7 +36,7 @@ pyinstaller = "^6.3.0"
[tool.poetry.group.test.dependencies]
pre-commit = "^3.3.3"
pytest = "^8.0.0"
pytest-cov = "^4.1.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.11.1"
responses = "^0.25.0"

Expand Down
31 changes: 20 additions & 11 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
aiohttp==3.9.5 ; python_version >= "3.9" and python_version < "3.13"
aiosignal==1.3.1 ; python_version >= "3.9" and python_version < "3.13"
anyio==4.3.0 ; python_version >= "3.9" and python_version < "3.13"
apprise==1.7.3 ; python_version >= "3.9" and python_version < "3.13"
apprise==1.7.6 ; python_version >= "3.9" and python_version < "3.13"
async-timeout==4.0.3 ; python_version >= "3.9" and python_version < "3.11"
attrs==23.2.0 ; python_version >= "3.9" and python_version < "3.13"
cachetools==5.3.3 ; python_version >= "3.9" and python_version < "3.13"
certifi==2024.2.2 ; python_version >= "3.9" and python_version < "3.13"
charset-normalizer==3.3.2 ; python_version >= "3.9" and python_version < "3.13"
click==8.1.7 ; python_version >= "3.9" and python_version < "3.13"
colorama==0.4.6 ; python_version >= "3.9" and python_version < "3.13" and (sys_platform == "win32" or platform_system == "Windows")
colorlog==6.8.2 ; python_version >= "3.9" and python_version < "3.13"
cron-descriptor==1.4.3 ; python_version >= "3.9" and python_version < "3.13"
exceptiongroup==1.2.0 ; python_version >= "3.9" and python_version < "3.11"
discord-py==2.3.2 ; python_version >= "3.9" and python_version < "3.13"
discord==2.3.2 ; python_version >= "3.9" and python_version < "3.13"
exceptiongroup==1.2.1 ; python_version >= "3.9" and python_version < "3.11"
frozenlist==1.4.1 ; python_version >= "3.9" and python_version < "3.13"
googlemaps==4.10.0 ; python_version >= "3.9" and python_version < "3.13"
h11==0.14.0 ; python_version >= "3.9" and python_version < "3.13"
httpcore==1.0.4 ; python_version >= "3.9" and python_version < "3.13"
httpcore==1.0.5 ; python_version >= "3.9" and python_version < "3.13"
httpx==0.27.0 ; python_version >= "3.9" and python_version < "3.13"
humanize==4.9.0 ; python_version >= "3.9" and python_version < "3.13"
idna==3.6 ; python_version >= "3.9" and python_version < "3.13"
importlib-metadata==7.0.2 ; python_version >= "3.9" and python_version < "3.10"
markdown==3.5.2 ; python_version >= "3.9" and python_version < "3.13"
idna==3.7 ; python_version >= "3.9" and python_version < "3.13"
importlib-metadata==7.1.0 ; python_version >= "3.9" and python_version < "3.10"
markdown==3.6 ; python_version >= "3.9" and python_version < "3.13"
multidict==6.0.5 ; python_version >= "3.9" and python_version < "3.13"
oauthlib==3.2.2 ; python_version >= "3.9" and python_version < "3.13"
packaging==23.2 ; python_version >= "3.9" and python_version < "3.13"
packaging==24.0 ; python_version >= "3.9" and python_version < "3.13"
progress==1.6 ; python_version >= "3.9" and python_version < "3.13"
prometheus-client==0.20.0 ; python_version >= "3.9" and python_version < "3.13"
pycron==3.0.0 ; python_version >= "3.9" and python_version < "3.13"
python-pushsafer==1.1 ; python_version >= "3.9" and python_version < "3.13"
python-telegram-bot[callback-data]==21.0.1 ; python_version >= "3.9" and python_version < "3.13"
python-telegram-bot[callback-data]==21.1.1 ; python_version >= "3.9" and python_version < "3.13"
pyyaml==6.0.1 ; python_version >= "3.9" and python_version < "3.13"
requests-oauthlib==1.3.1 ; python_version >= "3.9" and python_version < "3.13"
requests-oauthlib==2.0.0 ; python_version >= "3.9" and python_version < "3.13"
requests==2.31.0 ; python_version >= "3.9" and python_version < "3.13"
sniffio==1.3.1 ; python_version >= "3.9" and python_version < "3.13"
typing-extensions==4.10.0 ; python_version >= "3.9" and python_version < "3.11"
typing-extensions==4.11.0 ; python_version >= "3.9" and python_version < "3.11"
urllib3==2.2.1 ; python_version >= "3.9" and python_version < "3.13"
zipp==3.17.0 ; python_version >= "3.9" and python_version < "3.10"
yarl==1.9.4 ; python_version >= "3.9" and python_version < "3.13"
zipp==3.18.1 ; python_version >= "3.9" and python_version < "3.10"
39 changes: 39 additions & 0 deletions tests/test_notifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from tgtg_scanner.models import Config, Cron, Favorites, Item, Reservations
from tgtg_scanner.notifiers.apprise import Apprise
from tgtg_scanner.notifiers.console import Console
from tgtg_scanner.notifiers.discord import Discord
from tgtg_scanner.notifiers.ifttt import IFTTT
from tgtg_scanner.notifiers.ntfy import Ntfy
from tgtg_scanner.notifiers.script import Script
Expand Down Expand Up @@ -323,3 +324,41 @@ def test_telegram(test_item: Item, reservations: Reservations, favorites: Favori
assert telegram.thread.is_alive()
telegram.stop()
assert not telegram.thread.is_alive()


@pytest.fixture
def mocked_discord(mocker: MockerFixture):
mocker.patch(
"discord.ext.commands.Bot.login",
return_value=None,
)
mocker.patch(
"discord.ext.commands.Bot.start",
return_value=None,
)
mocker.patch(
"discord.ext.commands.Bot.command",
return_value=MagicMock(),
)
mocker.patch(
"discord.ext.commands.Bot.event",
return_value=MagicMock(),
)
mocker.patch(
"discord.ext.commands.Bot.dispatch",
return_value=None,
)
return mocker


def test_discord(test_item: Item, reservations: Reservations, favorites: Favorites, mocked_discord):
config = Config()
config.discord.enabled = True
config.discord.channel = 123456789012345678
config.discord.token = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.123456.ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKL"

discord = Discord(config, reservations, favorites)
discord.start()
discord.send(test_item)
sleep(0.5)
discord.stop()
4 changes: 3 additions & 1 deletion tgtg_scanner/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import argparse
import datetime
import http.client as http_client
import json
import logging
Expand Down Expand Up @@ -289,7 +290,8 @@ def _print_welcome_message() -> None:
log.info(line)
log.info("")
log.info("Version %s", __version__)
log.info("©2022, %s", __author__)
today = datetime.date.today()
log.info("©%s, %s", today.year, __author__)
log.info("For documentation and support please visit %s", __url__)
log.info("")

Expand Down
6 changes: 6 additions & 0 deletions tgtg_scanner/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,9 @@ class LocationConfigurationError(ConfigurationError):
def __init__(self, message="Invalid Location configuration"):
self.message = message
super().__init__(self.message)


class DiscordConfigurationError(ConfigurationError):
def __init__(self, message="Invalid Discord configuration"):
self.message = message
super().__init__(self.message)
35 changes: 35 additions & 0 deletions tgtg_scanner/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,38 @@ def _read_env(self):
self._env_get("SCRIPT_COMMAND", "command")


@dataclass
class DiscordConfig(NotifierConfig):
"""Discord configuration"""

enabled: bool = False
prefix: Union[str, None] = "!"
token: Union[str, None] = None
channel: int = 0
body: str = (
"*${{display_name}}*\n*Available*: ${{items_available}}\n*Price*: ${{price}} ${{currency}}\n*Pickup*: ${{pickupdate}}"
)
disable_commands: bool = False

def _read_ini(self, parser: configparser.ConfigParser):
self._ini_get_boolean(parser, "DISCORD", "Enabled", "enabled")
self._ini_get(parser, "DISCORD", "Prefix", "prefix")
self._ini_get(parser, "DISCORD", "Token", "token")
self._ini_get_int(parser, "DISCORD", "Channel", "channel")
self._ini_get(parser, "DISCORD", "Body", "body")
self._ini_get_boolean(parser, "DISCORD", "DisableCommands", "disable_commands")
self._ini_get_cron(parser, "DISCORD", "Cron", "cron")

def _read_env(self):
self._env_get_boolean("DISCORD", "enabled")
self._env_get("DISCORD_PREFIX", "prefix")
self._env_get("DISCORD_TOKEN", "token")
self._env_get_int("DISCORD_CHANNEL", "channel")
self._env_get("DISCORD_BODY", "body")
self._env_get_boolean("DISCORD_DISABLE_COMMANDS", "disable_commands")
self._env_get_cron("DISCORD_CRON", "cron")


@dataclass
class TgtgConfig(BaseConfig):
"""Tgtg configuration"""
Expand Down Expand Up @@ -525,6 +557,7 @@ class Config(BaseConfig):
ntfy: NtfyConfig = field(default_factory=NtfyConfig)
webhook: WebhookConfig = field(default_factory=WebhookConfig)
script: ScriptConfig = field(default_factory=ScriptConfig)
discord: DiscordConfig = field(default_factory=DiscordConfig)

def __post_init__(self):
if self.file:
Expand All @@ -546,6 +579,7 @@ def __post_init__(self):
self.ntfy._read_ini(parser)
self.webhook._read_ini(parser)
self.script._read_ini(parser)
self.discord._read_ini(parser)

log.info("Loaded config from %s", config_file.absolute())
else:
Expand All @@ -561,6 +595,7 @@ def __post_init__(self):
self.ntfy._read_env()
self.webhook._read_env()
self.script._read_env()
self.discord._read_env()

log.info("Loaded config from environment variables")

Expand Down
Loading