Skip to content

Commit

Permalink
Merge branch 'master' into dev/steam-py-lib
Browse files Browse the repository at this point in the history
  • Loading branch information
SyberiaK authored May 19, 2024
2 parents 7ae8d35 + 8c7a500 commit 2eabd4a
Show file tree
Hide file tree
Showing 24 changed files with 663 additions and 487 deletions.
14 changes: 6 additions & 8 deletions bottypes/botclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,7 @@ def signal_handler(signum, __):
task = asyncio.create_task(asyncio.sleep(self.MAINLOOP_TIMEOUT.total_seconds()))
try:
await task
if not self.logger.is_queue_empty():
await self.logger.process_queue()

await self.logger.process_queue()
except asyncio.CancelledError:
self.is_in_mainloop = False

Expand Down Expand Up @@ -458,31 +456,31 @@ async def log(self, text: str, *, disable_notification: bool = True,
"""Sends log to the log channel."""

if instant: # made for specific log messages (e.g. "Bot is shutting down...")
await self.logger.log_instantly(self, text, disable_notification, reply_markup, parse_mode)
await self.logger.send_log(self, text, disable_notification, reply_markup, parse_mode)
return

await self.logger.log(self, text, disable_notification, reply_markup, parse_mode)
await self.logger.schedule_system_log(self, text, disable_notification, reply_markup, parse_mode)

async def log_message(self, session: UserSession, message: Message):
"""Sends message log to the log channel."""

if self.test_mode:
return

await self.logger.log_message(self, session, message)
await self.logger.schedule_message_log(self, session, message)

async def log_callback(self, session: UserSession, callback_query: CallbackQuery):
"""Sends callback query log to the log channel."""

if self.test_mode:
return

await self.logger.log_callback(self, session, callback_query)
await self.logger.schedule_callback_log(self, session, callback_query)

async def log_inline(self, session: UserSession, inline_query: InlineQuery):
"""Sends an inline query log to the log channel."""

if self.test_mode:
return

await self.logger.log_inline(self, session, inline_query)
await self.logger.schedule_inline_log(self, session, inline_query)
129 changes: 74 additions & 55 deletions bottypes/logger.py
Original file line number Diff line number Diff line change
@@ -1,95 +1,114 @@
from __future__ import annotations

import asyncio
import typing

if typing.TYPE_CHECKING:
from typing import Coroutine
from pyrogram.enums import ParseMode

from pyrogram.types import (CallbackQuery, InlineQuery, Message,
ReplyKeyboardMarkup)
ReplyKeyboardMarkup, User)

from .botclient import BotClient
from .sessions import UserSession


class SystemLogPayload(typing.NamedTuple):
client: BotClient
text: str
disable_notification: bool
reply_markup: ReplyKeyboardMarkup
parse_mode: ParseMode


class EventLogPayload(typing.NamedTuple):
client: BotClient
user: User
session: UserSession
result_text: str


SYSTEM = 'system'


class BotLogger:
"""Made to work in a pair with BotClient handling logging stuff."""

def __init__(self, log_channel_id: int, ):
def __init__(self, log_channel_id: int):
self.log_channel_id = log_channel_id
self._logs_queue = asyncio.Queue()
self._logs_queue: dict[str, list[SystemLogPayload | EventLogPayload]] = {}

def is_queue_empty(self):
return self._logs_queue.empty()
return not bool(self._logs_queue)

def put_into_queue(self, _id: str, payload: SystemLogPayload | EventLogPayload):
if self._logs_queue.get(_id) is None:
self._logs_queue[_id] = []

async def put_into_queue(self, coroutine: Coroutine):
await self._logs_queue.put(coroutine)
self._logs_queue[_id].append(payload)

async def process_queue(self):
if not self.is_queue_empty():
coroutine = await self._logs_queue.get()
await coroutine

async def log(self, client: BotClient, text: str,
disable_notification: bool = True,
reply_markup: ReplyKeyboardMarkup = None,
parse_mode: ParseMode = None):
"""Put sending a log into the queue."""

await self._logs_queue.put(self.log_instantly(client, text,
disable_notification,
reply_markup,
parse_mode))

async def log_instantly(self, client: BotClient, text: str,
disable_notification: bool = True,
reply_markup: ReplyKeyboardMarkup = None,
parse_mode: ParseMode = None):
"""Sends log to the log channel instantly."""
userid = tuple(self._logs_queue)[0]
logged_events = self._logs_queue[userid]

if userid == SYSTEM: # invoked by system, not user
system_log = logged_events.pop(0)
return await self.send_log(system_log.client,
system_log.text,
system_log.disable_notification,
system_log.reply_markup,
system_log.parse_mode)

del self._logs_queue[userid]
client = logged_events[-1].client
user = logged_events[-1].user
session = logged_events[-1].session
display_name = f'@{user.username}' if user.username is not None else f'{user.mention} (username hidden)'

text = [f'👤: {display_name}',
f'ℹ️: {userid}',
f'✈️: {user.language_code}',
f'⚙️: {session.locale.lang_code}',
f'━━━━━━━━━━━━━━━━━━━━━━━'] + [event.result_text for event in logged_events]
return await self.send_log(client, '\n'.join(text), disable_notification=True)

async def schedule_system_log(self, client: BotClient, text: str,
disable_notification: bool = True,
reply_markup: ReplyKeyboardMarkup = None,
parse_mode: ParseMode = None):
"""Put sending a system log into the queue."""

self.put_into_queue(SYSTEM, SystemLogPayload(client, text, disable_notification, reply_markup, parse_mode))

async def send_log(self, client: BotClient, text: str,
disable_notification: bool = True,
reply_markup: ReplyKeyboardMarkup = None,
parse_mode: ParseMode = None):
"""Sends log to the log channel immediately, avoiding the queue."""

await client.send_message(self.log_channel_id, text,
disable_notification=disable_notification,
reply_markup=reply_markup,
parse_mode=parse_mode)

async def log_message(self, client: BotClient, session: UserSession, message: Message):
async def schedule_message_log(self, client: BotClient, session: UserSession, message: Message):
"""Put sending a message log into the queue."""

username = message.from_user.username
display_name = f'@{username}' if username is not None else f'{message.from_user.mention} (username hidden)'
user = message.from_user
message_text = message.text if message.text is not None else ""

text = (f'✍️ User: {display_name}\n'
f'ID: {message.from_user.id}\n'
f'Telegram language: {message.from_user.language_code}\n'
f'Chosen language: {session.locale.lang_code}\n'
f'Private message: "{message.text if message.text is not None else ""}"')
await self._logs_queue.put(client.send_message(self.log_channel_id, text, disable_notification=True))
self.put_into_queue(str(message.from_user.id), EventLogPayload(client, user, session, f'✍️: "{message_text}"'))

async def log_callback(self, client: BotClient, session: UserSession, callback_query: CallbackQuery):
async def schedule_callback_log(self, client: BotClient, session: UserSession, callback_query: CallbackQuery):
"""Put sending a callback query log into the queue"""

username = callback_query.from_user.username
display_name = f'@{username}' if username is not None \
else f'{callback_query.from_user.mention} (username hidden)'
user = callback_query.from_user

text = (f'🔀 User: {display_name}\n'
f'ID: {callback_query.from_user.id}\n'
f'Telegram language: {callback_query.from_user.language_code}\n'
f'Chosen language: {session.locale.lang_code}\n'
f'Callback query: {callback_query.data}')
await self._logs_queue.put(client.send_message(self.log_channel_id, text, disable_notification=True))
self.put_into_queue(str(user.id), EventLogPayload(client, user, session, f'🔀: {callback_query.data}'))

async def log_inline(self, client: BotClient, session: UserSession, inline_query: InlineQuery):
async def schedule_inline_log(self, client: BotClient, session: UserSession, inline_query: InlineQuery):
"""Put sending an inline query log into the queue."""

username = inline_query.from_user.username
display_name = f'@{username}' if username is not None else f'{inline_query.from_user.mention} (username hidden)'
user = inline_query.from_user

text = (f'🛰 User: {display_name}\n'
f'ID: {inline_query.from_user.id}\n'
f'Telegram language: {inline_query.from_user.language_code}\n'
f'Chosen language: {session.locale.lang_code}\n'
f'Inline query: "{inline_query.query}"')
await self._logs_queue.put(client.send_message(self.log_channel_id, text, disable_notification=True))
self.put_into_queue(str(user.id), EventLogPayload(client, user, session, f'🛰: "{inline_query.query}"'))
50 changes: 40 additions & 10 deletions core.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
('india', 'mumbai'): 'India Mumbai',
('india', 'chennai'): 'India Chennai',
('india', 'bombay'): 'India Bombay',
('india', 'madras'): 'India Madras',
('china', 'shanghai'): 'China Shanghai',
('china', 'tianjin'): 'China Tianjin',
('china', 'guangzhou'): 'China Guangzhou',
Expand All @@ -57,6 +58,27 @@
}

execution_start_dt = dt.datetime.now()

UNUSED_FIELDS = ['csgo_client_version',
'csgo_server_version',
'csgo_patch_version',
'csgo_version_timestamp',
'sdk_build_id',
'ds_build_id',
'valve_ds_changenumber',
'webapi',
'sessions_logon',
'steam_community',
'matchmaking_scheduler']

execution_start_dt = dt.datetime.now()

execution_cron_hour = execution_start_dt.hour
execution_cron_minute = execution_start_dt.minute + 1
if execution_cron_minute >= 60:
execution_cron_hour += 1
execution_cron_minute %= 60

loc = locale('ru')

logging.basicConfig(level=logging.INFO,
Expand All @@ -72,6 +94,12 @@
workdir=config.SESS_FOLDER)


def clear_from_unused_fields(cache: dict):
for field in UNUSED_FIELDS:
if cache.get(field):
del cache[field]


def remap_dc(info: dict, dc: Datacenter):
api_info_field = DATACENTER_API_FIELDS[dc.id]
return info[api_info_field]
Expand Down Expand Up @@ -122,6 +150,8 @@ async def update_cache_info():
with open(config.CACHE_FILE_PATH, encoding='utf-8') as f:
cache = json.load(f)

clear_from_unused_fields(cache)

overall_data = GameServers.request()

for key, value in overall_data.asdict().items():
Expand Down Expand Up @@ -150,12 +180,12 @@ async def update_cache_info():
cache['player_24h_peak'] = player_24h_peak

with open(config.CACHE_FILE_PATH, 'w', encoding='utf-8') as f:
json.dump(cache, f, indent=4)
json.dump(cache, f, indent=4, ensure_ascii=False)
except Exception:
logging.exception('Caught exception in the main thread!')


@scheduler.scheduled_job('cron', hour=execution_start_dt.hour, minute=execution_start_dt.minute + 1)
@scheduler.scheduled_job('cron', hour=execution_cron_hour, minute=execution_cron_minute)
async def unique_monthly():
# noinspection PyBroadException
try:
Expand All @@ -173,14 +203,14 @@ async def unique_monthly():
cache['monthly_unique_players'] = data

with open(config.CACHE_FILE_PATH, 'w', encoding='utf-8') as f:
json.dump(cache, f, indent=4)
json.dump(cache, f, indent=4, ensure_ascii=False)
except Exception:
logging.exception('Caught exception while gathering monthly players!')
time.sleep(45)
return await unique_monthly()


@scheduler.scheduled_job('cron', hour=execution_start_dt.hour, minute=execution_start_dt.minute + 1)
@scheduler.scheduled_job('cron', hour=execution_cron_hour, minute=execution_cron_minute)
async def check_currency():
# noinspection PyBroadException
try:
Expand All @@ -193,14 +223,14 @@ async def check_currency():
cache['key_price'] = new_prices

with open(config.CACHE_FILE_PATH, 'w', encoding='utf-8') as f:
json.dump(cache, f, indent=4)
json.dump(cache, f, indent=4, ensure_ascii=False)
except Exception:
logging.exception('Caught exception while gathering key price!')
time.sleep(45)
return await check_currency()


@scheduler.scheduled_job('cron', hour=execution_start_dt.hour, minute=execution_start_dt.minute + 2)
@scheduler.scheduled_job('cron', hour=execution_cron_hour, minute=execution_cron_minute, second=30)
async def fetch_leaderboard():
# noinspection PyBroadException
try:
Expand Down Expand Up @@ -248,10 +278,10 @@ async def send_alert(key, new_value):
logging.warning(f'Got wrong key to send alert: {key}')
return

if not config.TEST_MODE:
chat_list = [config.INCS2CHAT, config.CSTRACKER]
else:
if config.TEST_MODE:
chat_list = [config.AQ]
else:
chat_list = [config.INCS2CHAT, config.CSTRACKER]

for chat_id in chat_list:
msg = await bot.send_message(chat_id, text)
Expand Down
3 changes: 2 additions & 1 deletion game_coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import datetime as dt
import json
import logging
from pathlib import Path
import platform
import sys
import time
Expand Down Expand Up @@ -254,4 +255,4 @@ async def main():


if __name__ == '__main__':
asyncio.run(main())
asyncio.run(main())
Loading

0 comments on commit 2eabd4a

Please sign in to comment.