mirror of
https://github.com/skylanix/MamieHenriette.git
synced 2026-02-06 14:50:34 +01:00
Compare commits
1 Commits
main
...
api-timeou
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45347be85b |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,4 +3,5 @@
|
|||||||
**/.venv
|
**/.venv
|
||||||
__pycache__
|
__pycache__
|
||||||
instance
|
instance
|
||||||
|
logs
|
||||||
.tio.tokens.json
|
.tio.tokens.json
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ WORKDIR /app
|
|||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
ENV LANG=fr_FR.UTF-8
|
ENV LANG=fr_FR.UTF-8
|
||||||
ENV LC_ALL=fr_FR.UTF-8
|
ENV LC_ALL=fr_FR.UTF-8
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
apt-utils \
|
apt-utils \
|
||||||
@@ -34,7 +35,7 @@ RUN python3 -m venv /app/venv && \
|
|||||||
chmod +x /start.sh && \
|
chmod +x /start.sh && \
|
||||||
mkdir -p /app/logs
|
mkdir -p /app/logs
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
HEALTHCHECK --interval=1s --timeout=10s --start-period=5s --retries=3 \
|
||||||
CMD pgrep python > /dev/null && ! (tail -n 1000 $(ls -t /app/logs/*.log 2>/dev/null | head -1) 2>/dev/null | grep -iE "(ERROR|CRITICAL|Exception|sqlite3\.OperationalError)")
|
CMD pgrep python > /dev/null && ! (tail -n 1000 $(ls -t /app/logs/*.log 2>/dev/null | head -1) 2>/dev/null | grep -iE "(ERROR|CRITICAL|Exception|sqlite3\.OperationalError)")
|
||||||
|
|
||||||
CMD ["/start.sh"]
|
CMD ["/start.sh"]
|
||||||
|
|||||||
@@ -44,10 +44,13 @@ class DiscordBot(discord.Client):
|
|||||||
return channels
|
return channels
|
||||||
|
|
||||||
|
|
||||||
def begin(self) :
|
def begin(self) :
|
||||||
token = Configuration.query.filter_by(key='discord_token').first()
|
token = Configuration.query.filter_by(key='discord_token').first()
|
||||||
if token :
|
if token :
|
||||||
self.run(token.value)
|
try:
|
||||||
|
self.run(token.value)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Erreur fatale lors du démarrage du bot Discord : {e}')
|
||||||
else :
|
else :
|
||||||
logging.error('Aucun token Discord configuré. Le bot ne peut pas être démarré')
|
logging.error('Aucun token Discord configuré. Le bot ne peut pas être démarré')
|
||||||
|
|
||||||
@@ -66,8 +69,10 @@ async def on_message(message: Message):
|
|||||||
commande = Commande.query.filter_by(discord_enable=True, trigger=command_name).first()
|
commande = Commande.query.filter_by(discord_enable=True, trigger=command_name).first()
|
||||||
if commande:
|
if commande:
|
||||||
try:
|
try:
|
||||||
await message.channel.send(commande.response, suppress_embeds=True)
|
await asyncio.wait_for(message.channel.send(commande.response, suppress_embeds=True), timeout=30.0)
|
||||||
return
|
return
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logging.error(f'Timeout lors de l\'envoi de la commande Discord : {command_name}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f'Échec de l\'exécution de la commande Discord : {e}')
|
logging.error(f'Échec de l\'exécution de la commande Discord : {e}')
|
||||||
|
|
||||||
@@ -89,7 +94,9 @@ async def on_message(message: Message):
|
|||||||
if (rest > 0):
|
if (rest > 0):
|
||||||
msg += f'- et encore {rest} autres jeux'
|
msg += f'- et encore {rest} autres jeux'
|
||||||
try :
|
try :
|
||||||
await message.channel.send(msg, suppress_embeds=True)
|
await asyncio.wait_for(message.channel.send(msg, suppress_embeds=True), timeout=30.0)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logging.error(f'Timeout lors de l\'envoi du message ProtonDB')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f'Échec de l\'envoi du message ProtonDB : {e}')
|
logging.error(f'Échec de l\'envoi du message ProtonDB : {e}')
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
@@ -14,11 +15,18 @@ def _isEnable():
|
|||||||
return helper.getValue('humble_bundle_enable') and helper.getIntValue('humble_bundle_channel') != 0
|
return helper.getValue('humble_bundle_enable') and helper.getIntValue('humble_bundle_channel') != 0
|
||||||
|
|
||||||
def _callGithub():
|
def _callGithub():
|
||||||
response = requests.get("https://raw.githubusercontent.com/shionn/HumbleBundleGamePack/refs/heads/master/data/game-bundles.json")
|
try:
|
||||||
if response.status_code == 200:
|
response = requests.get("https://raw.githubusercontent.com/shionn/HumbleBundleGamePack/refs/heads/master/data/game-bundles.json", timeout=30)
|
||||||
return response.json()
|
if response.status_code == 200:
|
||||||
logging.error(f"Échec de la connexion à la ressource Humble Bundle. Code de statut HTTP : {response.status_code}")
|
return response.json()
|
||||||
return None
|
logging.error(f"Échec de la connexion à la ressource Humble Bundle. Code de statut HTTP : {response.status_code}")
|
||||||
|
return None
|
||||||
|
except (requests.exceptions.SSLError, requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
|
||||||
|
logging.error(f"Erreur de connexion à la ressource Humble Bundle : {e}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Erreur inattendue lors de la récupération des bundles : {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
def _isNotAlreadyNotified(bundle):
|
def _isNotAlreadyNotified(bundle):
|
||||||
return GameBundle.query.filter_by(url=bundle['url']).first() == None
|
return GameBundle.query.filter_by(url=bundle['url']).first() == None
|
||||||
@@ -46,9 +54,14 @@ async def checkHumbleBundleAndNotify(bot: Client):
|
|||||||
bundle = _findFirstNotNotified(bundles)
|
bundle = _findFirstNotNotified(bundles)
|
||||||
if bundle != None :
|
if bundle != None :
|
||||||
message = _formatMessage(bundle)
|
message = _formatMessage(bundle)
|
||||||
await bot.get_channel(ConfigurationHelper().getIntValue('humble_bundle_channel')).send(message)
|
try:
|
||||||
db.session.add(GameBundle(url=bundle['url'], name=bundle['name'], json = json.dumps(bundle)))
|
await asyncio.wait_for(bot.get_channel(ConfigurationHelper().getIntValue('humble_bundle_channel')).send(message), timeout=30.0)
|
||||||
db.session.commit()
|
db.session.add(GameBundle(url=bundle['url'], name=bundle['name'], json = json.dumps(bundle)))
|
||||||
|
db.session.commit()
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logging.error(f'Timeout lors de l\'envoi du message Humble Bundle')
|
||||||
|
except Exception as send_error:
|
||||||
|
logging.error(f'Erreur lors de l\'envoi du message Humble Bundle : {send_error}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Échec de la vérification des offres Humble Bundle : {e}")
|
logging.error(f"Échec de la vérification des offres Humble Bundle : {e}")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -8,24 +8,38 @@ from database.helpers import ConfigurationHelper
|
|||||||
from database.models import GameAlias
|
from database.models import GameAlias
|
||||||
from sqlalchemy import desc,func
|
from sqlalchemy import desc,func
|
||||||
|
|
||||||
def _call_algoliasearch(search_name:str):
|
def _call_algoliasearch(search_name:str):
|
||||||
config = SearchConfig(ConfigurationHelper().getValue('proton_db_api_id'),
|
try:
|
||||||
ConfigurationHelper().getValue('proton_db_api_key'))
|
config = SearchConfig(ConfigurationHelper().getValue('proton_db_api_id'),
|
||||||
config.set_default_hosts()
|
ConfigurationHelper().getValue('proton_db_api_key'))
|
||||||
client = SearchClientSync(config=config)
|
config.set_default_hosts()
|
||||||
return client.search_single_index(index_name="steamdb",
|
client = SearchClientSync(config=config)
|
||||||
search_params={
|
return client.search_single_index(index_name="steamdb",
|
||||||
"query":search_name,
|
search_params={
|
||||||
"facetFilters":[["appType:Game"]],
|
"query":search_name,
|
||||||
"hitsPerPage":50},
|
"facetFilters":[["appType:Game"]],
|
||||||
request_options= {'headers':{'Referer':'https://www.protondb.com/'}})
|
"hitsPerPage":50},
|
||||||
|
request_options= {
|
||||||
|
'headers':{'Referer':'https://www.protondb.com/'},
|
||||||
|
'timeout': 30
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Erreur lors de la recherche Algolia pour "{search_name}" : {e}')
|
||||||
|
return None
|
||||||
|
|
||||||
def _call_summary(id):
|
def _call_summary(id):
|
||||||
response = requests.get(f'http://jazzy-starlight-aeea19.netlify.app/api/v1/reports/summaries/{id}.json')
|
try:
|
||||||
if (response.status_code == 200) :
|
response = requests.get(f'http://jazzy-starlight-aeea19.netlify.app/api/v1/reports/summaries/{id}.json', timeout=30)
|
||||||
return response.json()
|
if (response.status_code == 200) :
|
||||||
logging.error(f'Échec de la récupération des données ProtonDB pour le jeu {id}. Code de statut HTTP : {response.status_code}')
|
return response.json()
|
||||||
return None
|
logging.error(f'Échec de la récupération des données ProtonDB pour le jeu {id}. Code de statut HTTP : {response.status_code}')
|
||||||
|
return None
|
||||||
|
except (requests.exceptions.SSLError, requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
|
||||||
|
logging.error(f'Erreur de connexion ProtonDB pour le jeu {id} : {e}')
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Erreur inattendue lors de la récupération ProtonDB pour le jeu {id} : {e}')
|
||||||
|
return None
|
||||||
|
|
||||||
def _is_name_match(name:str, search_name:str) -> bool:
|
def _is_name_match(name:str, search_name:str) -> bool:
|
||||||
normalized_game_name = re.sub("[^a-z0-9]", "", name.lower())
|
normalized_game_name = re.sub("[^a-z0-9]", "", name.lower())
|
||||||
@@ -37,10 +51,12 @@ def _apply_game_aliases(search_name:str) -> str:
|
|||||||
search_name = re.sub(re.escape(alias.alias), alias.name, search_name, flags=re.IGNORECASE)
|
search_name = re.sub(re.escape(alias.alias), alias.name, search_name, flags=re.IGNORECASE)
|
||||||
return search_name
|
return search_name
|
||||||
|
|
||||||
def searhProtonDb(search_name:str):
|
def searhProtonDb(search_name:str):
|
||||||
results = []
|
results = []
|
||||||
search_name = _apply_game_aliases(search_name)
|
search_name = _apply_game_aliases(search_name)
|
||||||
responses = _call_algoliasearch(search_name)
|
responses = _call_algoliasearch(search_name)
|
||||||
|
if responses is None:
|
||||||
|
return results
|
||||||
for hit in responses.model_dump().get('hits'):
|
for hit in responses.model_dump().get('hits'):
|
||||||
id = hit.get('object_id')
|
id = hit.get('object_id')
|
||||||
name:str = hit.get('name')
|
name:str = hit.get('name')
|
||||||
|
|||||||
@@ -37,14 +37,16 @@ class TwitchBot() :
|
|||||||
if _isConfigured() :
|
if _isConfigured() :
|
||||||
try :
|
try :
|
||||||
helper = ConfigurationHelper()
|
helper = ConfigurationHelper()
|
||||||
self.twitch = await Twitch(helper.getValue('twitch_client_id'), helper.getValue('twitch_client_secret'))
|
self.twitch = await asyncio.wait_for(Twitch(helper.getValue('twitch_client_id'), helper.getValue('twitch_client_secret')), timeout=30.0)
|
||||||
await self.twitch.set_user_authentication(helper.getValue('twitch_access_token'), USER_SCOPE, helper.getValue('twitch_refresh_token'))
|
await asyncio.wait_for(self.twitch.set_user_authentication(helper.getValue('twitch_access_token'), USER_SCOPE, helper.getValue('twitch_refresh_token')), timeout=30.0)
|
||||||
self.chat = await Chat(self.twitch)
|
self.chat = await asyncio.wait_for(Chat(self.twitch), timeout=30.0)
|
||||||
self.chat.register_event(ChatEvent.READY, _onReady)
|
self.chat.register_event(ChatEvent.READY, _onReady)
|
||||||
self.chat.register_event(ChatEvent.MESSAGE, _onMessage)
|
self.chat.register_event(ChatEvent.MESSAGE, _onMessage)
|
||||||
# chat.register_event(ChatEvent.SUB, on_sub)
|
# chat.register_event(ChatEvent.SUB, on_sub)
|
||||||
self.chat.register_command('hello', _helloCommand)
|
self.chat.register_command('hello', _helloCommand)
|
||||||
self.chat.start()
|
self.chat.start()
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logging.error('Timeout lors de la connexion à Twitch. Vérifiez votre connexion réseau.')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f'Échec de l\'authentification Twitch. Vérifiez vos identifiants et redémarrez après correction : {e}')
|
logging.error(f'Échec de l\'authentification Twitch. Vérifiez vos identifiants et redémarrez après correction : {e}')
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from twitchAPI.twitch import Twitch
|
from twitchAPI.twitch import Twitch
|
||||||
@@ -36,14 +37,24 @@ async def _notifyAlert(alert : LiveAlert, stream : Stream):
|
|||||||
|
|
||||||
async def _sendMessage(channel : int, message : str) :
|
async def _sendMessage(channel : int, message : str) :
|
||||||
logger.info(f'Envoi de notification : {message}')
|
logger.info(f'Envoi de notification : {message}')
|
||||||
await bot.get_channel(channel).send(message)
|
try:
|
||||||
logger.info(f'Notification envoyé')
|
await asyncio.wait_for(bot.get_channel(channel).send(message), timeout=30.0)
|
||||||
|
logger.info(f'Notification envoyée')
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logger.error(f'Timeout lors de l\'envoi de notification live alert')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Erreur lors de l\'envoi de notification live alert : {e}')
|
||||||
|
|
||||||
async def _retreiveStreams(twitch: Twitch, alerts : list[LiveAlert]) -> list[Stream] :
|
async def _retreiveStreams(twitch: Twitch, alerts : list[LiveAlert]) -> list[Stream] :
|
||||||
streams : list[Stream] = []
|
streams : list[Stream] = []
|
||||||
logger.info(f'Recherche de streams pour : {alerts}')
|
logger.info(f'Recherche de streams pour : {alerts}')
|
||||||
async for stream in twitch.get_streams(user_login = [alert.login for alert in alerts]):
|
try:
|
||||||
streams.append(stream)
|
async for stream in asyncio.wait_for(twitch.get_streams(user_login = [alert.login for alert in alerts]), timeout=30.0):
|
||||||
logger.info(f'Ces streams sont en ligne : {streams}')
|
streams.append(stream)
|
||||||
|
logger.info(f'Ces streams sont en ligne : {streams}')
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logger.error('Timeout lors de la récupération des streams Twitch')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Erreur lors de la récupération des streams Twitch : {e}')
|
||||||
return streams
|
return streams
|
||||||
|
|
||||||
|
|||||||
@@ -17,34 +17,53 @@ auth: UserAuthenticator
|
|||||||
def twitchConfigurationHelp():
|
def twitchConfigurationHelp():
|
||||||
return render_template("twitch-aide.html", token_redirect_url = _buildUrl())
|
return render_template("twitch-aide.html", token_redirect_url = _buildUrl())
|
||||||
|
|
||||||
@webapp.route("/configurations/twitch/request-token")
|
@webapp.route("/configurations/twitch/request-token")
|
||||||
async def twitchRequestToken():
|
async def twitchRequestToken():
|
||||||
global auth
|
global auth
|
||||||
helper = ConfigurationHelper()
|
try:
|
||||||
twitch = await Twitch(helper.getValue('twitch_client_id'), helper.getValue('twitch_client_secret'))
|
helper = ConfigurationHelper()
|
||||||
auth = UserAuthenticator(twitch, USER_SCOPE, url=_buildUrl())
|
import asyncio
|
||||||
return redirect(auth.return_auth_url())
|
twitch = await asyncio.wait_for(
|
||||||
|
Twitch(helper.getValue('twitch_client_id'), helper.getValue('twitch_client_secret')),
|
||||||
|
timeout=30.0
|
||||||
|
)
|
||||||
|
auth = UserAuthenticator(twitch, USER_SCOPE, url=_buildUrl())
|
||||||
|
return redirect(auth.return_auth_url())
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logging.error('Timeout lors de la connexion à Twitch API pour la demande de token')
|
||||||
|
return redirect(url_for('openConfigurations'))
|
||||||
|
except TwitchAPIException as e:
|
||||||
|
logging.error(f'Erreur API Twitch lors de la demande de token : {e}')
|
||||||
|
return redirect(url_for('openConfigurations'))
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Erreur inattendue lors de la demande de token Twitch : {e}')
|
||||||
|
return redirect(url_for('openConfigurations'))
|
||||||
|
|
||||||
@webapp.route("/configurations/twitch/receive-token")
|
@webapp.route("/configurations/twitch/receive-token")
|
||||||
async def twitchReceiveToken():
|
async def twitchReceiveToken():
|
||||||
global auth
|
global auth
|
||||||
state = request.args.get('state')
|
state = request.args.get('state')
|
||||||
code = request.args.get('code')
|
code = request.args.get('code')
|
||||||
if state != auth.state :
|
if state != auth.state :
|
||||||
logging('bad returned state')
|
logging.error('bad returned state')
|
||||||
return redirect(url_for('openConfigurations'))
|
return redirect(url_for('openConfigurations'))
|
||||||
if code == None :
|
if code == None :
|
||||||
logging('no returned state')
|
logging.error('no returned code')
|
||||||
return redirect(url_for('openConfigurations'))
|
return redirect(url_for('openConfigurations'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
token, refresh = await auth.authenticate(user_token=code)
|
import asyncio
|
||||||
|
token, refresh = await asyncio.wait_for(auth.authenticate(user_token=code), timeout=30.0)
|
||||||
helper = ConfigurationHelper()
|
helper = ConfigurationHelper()
|
||||||
helper.createOrUpdate('twitch_access_token', token)
|
helper.createOrUpdate('twitch_access_token', token)
|
||||||
helper.createOrUpdate('twitch_refresh_token', refresh)
|
helper.createOrUpdate('twitch_refresh_token', refresh)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logging.error('Timeout lors de l\'authentification Twitch')
|
||||||
except TwitchAPIException as e:
|
except TwitchAPIException as e:
|
||||||
logging(e)
|
logging.error(f'Erreur API Twitch lors de l\'authentification : {e}')
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Erreur inattendue lors de l\'authentification Twitch : {e}')
|
||||||
return redirect(url_for('openConfigurations'))
|
return redirect(url_for('openConfigurations'))
|
||||||
|
|
||||||
# hack pas fou mais on estime qu'on sera toujours en ssl en connecté
|
# hack pas fou mais on estime qu'on sera toujours en ssl en connecté
|
||||||
|
|||||||
Reference in New Issue
Block a user