fix logging messages and add TG listener
parent
36532fc1c9
commit
9745d580d4
BIN
database.db
BIN
database.db
Binary file not shown.
|
@ -109,7 +109,7 @@ class ExcelParser:
|
||||||
return {"price": price,
|
return {"price": price,
|
||||||
"vat": int(data['percent_vat']),
|
"vat": int(data['percent_vat']),
|
||||||
"max_days": int(data['maxdays']),
|
"max_days": int(data['maxdays']),
|
||||||
"transport_delivery_date": df["Дата загрузки"]}
|
"transport_delivery_date": str(df["Дата загрузки"][0])}
|
||||||
|
|
||||||
self.add_link_to_database(query, answer=data)
|
self.add_link_to_database(query, answer=data)
|
||||||
return None
|
return None
|
||||||
|
|
86
main.py
86
main.py
|
@ -18,6 +18,14 @@ from storage import Storage
|
||||||
from telegram_logs import logger
|
from telegram_logs import logger
|
||||||
|
|
||||||
import dotenv
|
import dotenv
|
||||||
|
import asyncio
|
||||||
|
import threading
|
||||||
|
from webserver import dp
|
||||||
|
|
||||||
|
from aiogram.filters import CommandStart
|
||||||
|
from aiogram.types import Message, ReplyKeyboardMarkup, KeyboardButton
|
||||||
|
|
||||||
|
from webserver import bot
|
||||||
|
|
||||||
dotenv.load_dotenv('.env')
|
dotenv.load_dotenv('.env')
|
||||||
IS_PROD = os.environ.get('PROD_ENV') == '1'
|
IS_PROD = os.environ.get('PROD_ENV') == '1'
|
||||||
|
@ -51,6 +59,7 @@ class Parser:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
if exc_val is not None:
|
||||||
logger.error("Бот остановлен. Причина: " + str(exc_val))
|
logger.error("Бот остановлен. Причина: " + str(exc_val))
|
||||||
print("Gracefully shutting down...")
|
print("Gracefully shutting down...")
|
||||||
|
|
||||||
|
@ -100,13 +109,18 @@ class Parser:
|
||||||
0] not in self.storage.get_links()]
|
0] not in self.storage.get_links()]
|
||||||
|
|
||||||
for link in links:
|
for link in links:
|
||||||
|
if PARSER_ALIVE is False:
|
||||||
|
raise KeyboardInterrupt("Бот остановлен по запросу")
|
||||||
logger.info("Обработка заявки: " + link)
|
logger.info("Обработка заявки: " + link)
|
||||||
try:
|
try:
|
||||||
self.accept_documentation(link)
|
self.accept_documentation(link)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("Не удалось обработать заявку. Подробности: " + str(exc))
|
logger.error("Не удалось обработать заявку. Подробности: " + str(exc))
|
||||||
|
logger.info("Все LTL заявки обработаны, обновление через 60сек")
|
||||||
|
|
||||||
def parse(self, url: str = None) -> dict:
|
def parse(self, url: str = None) -> dict:
|
||||||
|
if PARSER_ALIVE is False:
|
||||||
|
raise KeyboardInterrupt("Бот остановлен по запросу")
|
||||||
fp = self.download_documentation()
|
fp = self.download_documentation()
|
||||||
e_parser = ExcelParser(fp, url)
|
e_parser = ExcelParser(fp, url)
|
||||||
price = e_parser.calculate()
|
price = e_parser.calculate()
|
||||||
|
@ -116,6 +130,9 @@ class Parser:
|
||||||
return price
|
return price
|
||||||
|
|
||||||
def accept_documentation(self, url: str):
|
def accept_documentation(self, url: str):
|
||||||
|
if PARSER_ALIVE is False:
|
||||||
|
raise KeyboardInterrupt("Бот остановлен по запросу")
|
||||||
|
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
self._driver.get(url)
|
self._driver.get(url)
|
||||||
|
|
||||||
|
@ -142,6 +159,8 @@ class Parser:
|
||||||
delivery_range=price['max_days'])
|
delivery_range=price['max_days'])
|
||||||
|
|
||||||
def download_documentation(self) -> list[pathlib.Path]:
|
def download_documentation(self) -> list[pathlib.Path]:
|
||||||
|
if PARSER_ALIVE is False:
|
||||||
|
raise KeyboardInterrupt("Бот остановлен по запросу")
|
||||||
try:
|
try:
|
||||||
all_files_1 = set(
|
all_files_1 = set(
|
||||||
pathlib.Path('./downloads') / pathlib.Path(file) for tree in os.walk('./downloads') for file in tree[2])
|
pathlib.Path('./downloads') / pathlib.Path(file) for tree in os.walk('./downloads') for file in tree[2])
|
||||||
|
@ -168,6 +187,8 @@ class Parser:
|
||||||
raise KeyboardInterrupt()
|
raise KeyboardInterrupt()
|
||||||
|
|
||||||
def send_offer_link(self, price: int, nds: int, delivery_range: str, delivery_time: str):
|
def send_offer_link(self, price: int, nds: int, delivery_range: str, delivery_time: str):
|
||||||
|
if PARSER_ALIVE is False:
|
||||||
|
raise KeyboardInterrupt("Бот остановлен по запросу")
|
||||||
try:
|
try:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Предварительные данные по заявке: Цена: {price}, НДС: {nds}%, Доставка: {delivery_range} дн., Подача машины {delivery_time}")
|
f"Предварительные данные по заявке: Цена: {price}, НДС: {nds}%, Доставка: {delivery_range} дн., Подача машины {delivery_time}")
|
||||||
|
@ -220,10 +241,71 @@ class Parser:
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
PARSER_ALIVE = True
|
||||||
|
|
||||||
|
|
||||||
|
def parse_runner():
|
||||||
with Parser() as parser:
|
with Parser() as parser:
|
||||||
parser.login()
|
parser.login()
|
||||||
while True:
|
while True:
|
||||||
parser.search()
|
parser.search()
|
||||||
logger.info("Все LTL заявки обработаны, обновление через 60сек")
|
|
||||||
time.sleep(60)
|
time.sleep(60)
|
||||||
|
|
||||||
|
|
||||||
|
parser_thread = threading.Thread(target=parse_runner, daemon=True)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(CommandStart())
|
||||||
|
async def start_handler(message: Message):
|
||||||
|
s = Storage()
|
||||||
|
if message.from_user.id not in s.get_users():
|
||||||
|
await message.answer("Вы не зарегистрированы, обратитесь к администратору")
|
||||||
|
return
|
||||||
|
markup = ReplyKeyboardMarkup(keyboard=[[KeyboardButton(text="Запустить Бот")]])
|
||||||
|
await message.answer(f"Hello, {message.from_user.full_name}!", reply_markup=markup)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message()
|
||||||
|
async def message_handler(message: Message):
|
||||||
|
global PARSER_ALIVE
|
||||||
|
s = Storage()
|
||||||
|
if message.from_user.id not in s.get_users():
|
||||||
|
await message.answer("Вы не зарегистрированы, обратитесь к администратору")
|
||||||
|
return
|
||||||
|
|
||||||
|
markup = ReplyKeyboardMarkup(keyboard=[[KeyboardButton(text="Запустить Бот")]])
|
||||||
|
if message.text == "Запустить Бот":
|
||||||
|
PARSER_ALIVE = True
|
||||||
|
for chat_id in s.get_users():
|
||||||
|
await bot.send_message(chat_id,
|
||||||
|
f"Пользователь {message.from_user.full_name} запускает бот",
|
||||||
|
reply_markup=markup)
|
||||||
|
parser_thread.start()
|
||||||
|
return
|
||||||
|
if message.text == "Остановить Бот":
|
||||||
|
for chat_id in s.get_users():
|
||||||
|
await bot.send_message(chat_id,
|
||||||
|
f"Пользователь {message.from_user.full_name} остановил бот",
|
||||||
|
reply_markup=markup)
|
||||||
|
PARSER_ALIVE = False
|
||||||
|
await message.answer("Бот остановлен", reply_markup=markup)
|
||||||
|
return
|
||||||
|
await message.answer("Неизвестная команда")
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
storage = Storage()
|
||||||
|
markup = ReplyKeyboardMarkup(keyboard=[[KeyboardButton(text="Запустить Бот")]])
|
||||||
|
for chat_id in storage.get_users():
|
||||||
|
await bot.send_message(chat_id,
|
||||||
|
"Контроллер запущен, бот ожидает включения",
|
||||||
|
reply_markup=markup,
|
||||||
|
disable_notification=True)
|
||||||
|
await dp.start_polling(bot)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# with Parser() as p:
|
||||||
|
# p.login()
|
||||||
|
# p.search()
|
||||||
|
asyncio.run(main())
|
||||||
|
|
||||||
|
|
|
@ -12,3 +12,4 @@ pyexcel-xls
|
||||||
pyexcel-xlsx
|
pyexcel-xlsx
|
||||||
loguru
|
loguru
|
||||||
pydotenv
|
pydotenv
|
||||||
|
aiogram
|
|
@ -4,14 +4,28 @@
|
||||||
#
|
#
|
||||||
# pip-compile
|
# pip-compile
|
||||||
#
|
#
|
||||||
|
aiofiles==23.2.1
|
||||||
|
# via aiogram
|
||||||
|
aiogram==3.4.1
|
||||||
|
# via -r requirements.in
|
||||||
|
aiohttp==3.9.3
|
||||||
|
# via aiogram
|
||||||
|
aiosignal==1.3.1
|
||||||
|
# via aiohttp
|
||||||
|
annotated-types==0.6.0
|
||||||
|
# via pydantic
|
||||||
|
async-timeout==4.0.3
|
||||||
|
# via aiohttp
|
||||||
attrs==23.2.0
|
attrs==23.2.0
|
||||||
# via
|
# via
|
||||||
|
# aiohttp
|
||||||
# outcome
|
# outcome
|
||||||
# trio
|
# trio
|
||||||
beautifulsoup4==4.12.3
|
beautifulsoup4==4.12.3
|
||||||
# via xls2xlsx
|
# via xls2xlsx
|
||||||
certifi==2023.11.17
|
certifi==2023.11.17
|
||||||
# via
|
# via
|
||||||
|
# aiogram
|
||||||
# requests
|
# requests
|
||||||
# selenium
|
# selenium
|
||||||
chardet==5.2.0
|
chardet==5.2.0
|
||||||
|
@ -32,18 +46,29 @@ exceptiongroup==1.2.0
|
||||||
# trio-websocket
|
# trio-websocket
|
||||||
fonttools==4.47.2
|
fonttools==4.47.2
|
||||||
# via xls2xlsx
|
# via xls2xlsx
|
||||||
|
frozenlist==1.4.1
|
||||||
|
# via
|
||||||
|
# aiohttp
|
||||||
|
# aiosignal
|
||||||
h11==0.14.0
|
h11==0.14.0
|
||||||
# via wsproto
|
# via wsproto
|
||||||
idna==3.6
|
idna==3.6
|
||||||
# via
|
# via
|
||||||
# requests
|
# requests
|
||||||
# trio
|
# trio
|
||||||
|
# yarl
|
||||||
lml==0.1.0
|
lml==0.1.0
|
||||||
# via
|
# via
|
||||||
# pyexcel
|
# pyexcel
|
||||||
# pyexcel-io
|
# pyexcel-io
|
||||||
loguru==0.7.2
|
loguru==0.7.2
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
|
magic-filter==1.0.12
|
||||||
|
# via aiogram
|
||||||
|
multidict==6.0.5
|
||||||
|
# via
|
||||||
|
# aiohttp
|
||||||
|
# yarl
|
||||||
numpy==1.26.3
|
numpy==1.26.3
|
||||||
# via pandas
|
# via pandas
|
||||||
openpyxl==3.1.2
|
openpyxl==3.1.2
|
||||||
|
@ -59,6 +84,10 @@ pandas==2.2.0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
pillow==10.2.0
|
pillow==10.2.0
|
||||||
# via xls2xlsx
|
# via xls2xlsx
|
||||||
|
pydantic==2.5.3
|
||||||
|
# via aiogram
|
||||||
|
pydantic-core==2.14.6
|
||||||
|
# via pydantic
|
||||||
pydotenv==0.0.7
|
pydotenv==0.0.7
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
pyexcel==0.7.0
|
pyexcel==0.7.0
|
||||||
|
@ -108,7 +137,11 @@ trio==0.24.0
|
||||||
trio-websocket==0.11.1
|
trio-websocket==0.11.1
|
||||||
# via selenium
|
# via selenium
|
||||||
typing-extensions==4.9.0
|
typing-extensions==4.9.0
|
||||||
# via selenium
|
# via
|
||||||
|
# aiogram
|
||||||
|
# pydantic
|
||||||
|
# pydantic-core
|
||||||
|
# selenium
|
||||||
tzdata==2023.4
|
tzdata==2023.4
|
||||||
# via pandas
|
# via pandas
|
||||||
urllib3[socks]==2.1.0
|
urllib3[socks]==2.1.0
|
||||||
|
@ -137,3 +170,5 @@ xlwt==1.3.0
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# pyexcel-xls
|
# pyexcel-xls
|
||||||
# xlutils
|
# xlutils
|
||||||
|
yarl==1.9.4
|
||||||
|
# via aiohttp
|
||||||
|
|
|
@ -5,10 +5,15 @@ from typing import ContextManager
|
||||||
|
|
||||||
class Storage:
|
class Storage:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.con = None
|
||||||
|
|
||||||
|
def set_connection(self):
|
||||||
self.con = sqlite3.connect("database.db")
|
self.con = sqlite3.connect("database.db")
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def get_cursor(self) -> ContextManager[sqlite3.Cursor]:
|
def get_cursor(self) -> ContextManager[sqlite3.Cursor]:
|
||||||
|
if self.con is None:
|
||||||
|
self.set_connection()
|
||||||
cur = self.con.cursor()
|
cur = self.con.cursor()
|
||||||
try:
|
try:
|
||||||
yield cur
|
yield cur
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import json
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from storage import Storage
|
from storage import Storage
|
||||||
|
@ -19,11 +21,11 @@ def _log(message):
|
||||||
r = requests.post("{0}{1}/sendMessage".format(_URL, _TOKEN), {
|
r = requests.post("{0}{1}/sendMessage".format(_URL, _TOKEN), {
|
||||||
"chat_id": int(chat_id),
|
"chat_id": int(chat_id),
|
||||||
"disable_notification": True,
|
"disable_notification": True,
|
||||||
"text": icon + message
|
"text": icon + message,
|
||||||
|
"reply_markup": json.dumps({"keyboard": [[{"text": "Остановить Бот"}]]})
|
||||||
})
|
})
|
||||||
|
if r.status_code != 200:
|
||||||
if r.status_code >= 400:
|
print(r.json())
|
||||||
logger.error("Failed to send message: {0} {1}".format(r.status_code, r.text))
|
|
||||||
|
|
||||||
|
|
||||||
def _filter_info_only(record):
|
def _filter_info_only(record):
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
from aiogram import Bot, Dispatcher
|
||||||
|
|
||||||
|
dp = Dispatcher()
|
||||||
|
bot = Bot(token="6767909836:AAFpsqtWeBNIBgSSi2_19rltEHOF0mrvTg0")
|
Loading…
Reference in New Issue