freqtrade_origin/freqtrade/rpc/webhook.py

136 lines
4.8 KiB
Python
Raw Normal View History

2018-07-05 19:32:53 +00:00
"""
This module manages webhook communication
"""
2024-05-12 14:51:11 +00:00
2018-07-05 19:32:53 +00:00
import logging
import time
from typing import Any, Optional
2018-07-05 19:32:53 +00:00
2020-09-28 17:39:41 +00:00
from requests import RequestException, post
2018-07-05 19:32:53 +00:00
2022-09-18 11:31:52 +00:00
from freqtrade.constants import Config
2021-06-09 17:51:44 +00:00
from freqtrade.enums import RPCMessageType
from freqtrade.rpc import RPC, RPCHandler
from freqtrade.rpc.rpc_types import RPCSendMsg
2018-07-05 19:32:53 +00:00
logger = logging.getLogger(__name__)
2024-05-12 14:51:11 +00:00
logger.debug("Included module rpc.webhook ...")
2018-07-05 19:32:53 +00:00
class Webhook(RPCHandler):
2024-05-12 14:51:11 +00:00
"""This class handles all webhook communication"""
2018-07-05 19:32:53 +00:00
2022-09-18 11:31:52 +00:00
def __init__(self, rpc: RPC, config: Config) -> None:
2018-07-05 19:32:53 +00:00
"""
Init the Webhook class, and init the super class RPCHandler
:param rpc: instance of RPC Helper class
:param config: Configuration object
2018-07-05 19:32:53 +00:00
:return: None
"""
super().__init__(rpc, config)
2018-07-05 19:32:53 +00:00
2024-05-12 14:51:11 +00:00
self._url = self._config["webhook"]["url"]
self._format = self._config["webhook"].get("format", "form")
self._retries = self._config["webhook"].get("retries", 0)
self._retry_delay = self._config["webhook"].get("retry_delay", 0.1)
self._timeout = self._config["webhook"].get("timeout", 10)
2018-07-05 19:32:53 +00:00
def cleanup(self) -> None:
"""
Cleanup pending module resources.
This will do nothing for webhooks, they will simply not be called anymore
"""
pass
def _get_value_dict(self, msg: RPCSendMsg) -> Optional[dict[str, Any]]:
2024-05-12 14:51:11 +00:00
whconfig = self._config["webhook"]
if msg["type"].value in whconfig:
2023-04-27 16:27:09 +00:00
# Explicit types should have priority
2024-05-12 14:51:11 +00:00
valuedict = whconfig.get(msg["type"].value)
2022-10-07 18:52:14 +00:00
# Deprecated 2022.10 - only keep generic method.
2024-05-12 14:51:11 +00:00
elif msg["type"] in [RPCMessageType.ENTRY]:
valuedict = whconfig.get("webhookentry")
elif msg["type"] in [RPCMessageType.ENTRY_CANCEL]:
valuedict = whconfig.get("webhookentrycancel")
elif msg["type"] in [RPCMessageType.ENTRY_FILL]:
valuedict = whconfig.get("webhookentryfill")
elif msg["type"] == RPCMessageType.EXIT:
valuedict = whconfig.get("webhookexit")
elif msg["type"] == RPCMessageType.EXIT_FILL:
valuedict = whconfig.get("webhookexitfill")
elif msg["type"] == RPCMessageType.EXIT_CANCEL:
valuedict = whconfig.get("webhookexitcancel")
elif msg["type"] in (
RPCMessageType.STATUS,
RPCMessageType.STARTUP,
RPCMessageType.EXCEPTION,
RPCMessageType.WARNING,
):
valuedict = whconfig.get("webhookstatus")
elif msg["type"] in (
RPCMessageType.PROTECTION_TRIGGER,
RPCMessageType.PROTECTION_TRIGGER_GLOBAL,
RPCMessageType.WHITELIST,
RPCMessageType.ANALYZED_DF,
RPCMessageType.NEW_CANDLE,
RPCMessageType.STRATEGY_MSG,
):
2022-10-07 18:52:14 +00:00
# Don't fail for non-implemented types
return None
return valuedict
def send_msg(self, msg: RPCSendMsg) -> None:
2024-05-12 14:51:11 +00:00
"""Send a message to telegram channel"""
2018-07-05 19:32:53 +00:00
try:
2022-10-07 18:52:14 +00:00
valuedict = self._get_value_dict(msg)
2022-10-07 18:44:47 +00:00
2018-07-05 19:32:53 +00:00
if not valuedict:
2024-05-12 14:51:11 +00:00
logger.debug("Message type '%s' not configured for webhooks", msg["type"])
2018-07-05 19:32:53 +00:00
return
2018-07-12 19:54:31 +00:00
payload = {key: value.format(**msg) for (key, value) in valuedict.items()}
2018-07-05 19:32:53 +00:00
self._send_msg(payload)
except KeyError as exc:
2024-05-12 14:51:11 +00:00
logger.exception(
"Problem calling Webhook. Please check your webhook configuration. "
"Exception: %s",
exc,
)
2018-07-05 19:32:53 +00:00
def _send_msg(self, payload: dict) -> None:
2018-07-14 11:29:34 +00:00
"""do the actual call to the webhook"""
2018-07-05 19:32:53 +00:00
success = False
attempts = 0
while not success and attempts <= self._retries:
if attempts:
2021-11-29 18:54:54 +00:00
if self._retry_delay:
time.sleep(self._retry_delay)
logger.info("Retrying webhook...")
attempts += 1
try:
2024-05-12 14:51:11 +00:00
if self._format == "form":
2023-07-23 16:28:43 +00:00
response = post(self._url, data=payload, timeout=self._timeout)
2024-05-12 14:51:11 +00:00
elif self._format == "json":
2023-07-23 16:28:43 +00:00
response = post(self._url, json=payload, timeout=self._timeout)
2024-05-12 14:51:11 +00:00
elif self._format == "raw":
response = post(
self._url,
data=payload["data"],
headers={"Content-Type": "text/plain"},
timeout=self._timeout,
)
else:
2024-05-12 14:51:11 +00:00
raise NotImplementedError(f"Unknown format: {self._format}")
2021-11-29 18:54:54 +00:00
2021-11-28 23:30:41 +00:00
# Throw a RequestException if the post was not successful
response.raise_for_status()
success = True
except RequestException as exc:
logger.warning("Could not call webhook url. Exception: %s", exc)