Compare commits

...

2 Commits

Author SHA1 Message Date
20fa3eb8a1 Поправил ошибку с list has no attribute 'title' 2026-06-05 16:14:07 +03:00
63044be106 Revert "Бот + прокси"
This reverts commit 5dc9c8f0d1.
2026-06-04 16:00:35 +03:00
12 changed files with 57 additions and 151 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -59,8 +59,9 @@ class ClockifyAPI(ClockifyConfig):
if not tag:
tag = self.get_tags(True, tag_id=tag_id, **{"page-size": self.tags_count})
if tag:
db.insert_tags([tag] if isinstance(tag, Tag) else tag)
if isinstance(tag, Tag):
db.insert_tags([tag])
tags.append(tag)
from config import red
try:
task_id = parse_task_id(tag.title)
@@ -72,6 +73,7 @@ class ClockifyAPI(ClockifyConfig):
db.insert_tasks([task])
else:
logger.error(f"{Fore.RED} Тег не найден: {tag_id}{Fore.RESET}")
continue
else:
tag = tag[0]
task_id = parse_task_id(tag.title)
@@ -98,7 +100,7 @@ class ClockifyAPI(ClockifyConfig):
return result
def get_tags(self, is_updating=False, tag_id="", **kwargs) -> Union[list[Tag], Tag]:
def get_tags(self, is_updating=False, tag_id="", **kwargs) -> Union[list[Tag], Tag, None]:
"""
Получает с сервера Clockify теги
name: str - optional
@@ -108,26 +110,40 @@ class ClockifyAPI(ClockifyConfig):
"""
if is_updating:
page = 1
kwargs["page"] = page
tags = requests.get(self.base_url + self.tags_url, params={"sort-column": "name", "sort-order": "descending", **kwargs}, headers={"X-API-KEY": self.token}).json()
while len(tags) == self.tags_count:
if tag_id:
for tag in tags:
if tag_id == tag["id"]:
return Tag(
id=tag["id"],
title=tag["name"]
)
page += 1
kwargs["page"] = page
tags = requests.get(self.base_url + self.tags_url, params={"sort-column": "name", "sort-order": "descending", **kwargs}, headers={"X-API-KEY": self.token}).json()
else:
page_size = int(kwargs.get("page-size", self.tags_count))
result = []
while True:
params = {
"sort-column": "name",
"sort-order": "descending",
**kwargs,
"page": page
}
tags = requests.get(
self.base_url + self.tags_url,
params=params,
headers={"X-API-KEY": self.token}
).json()
for tag in tags:
clockify_tag = Tag(
id=tag["id"],
title=tag["name"]
)
if tag_id and tag_id == clockify_tag.id:
return clockify_tag
if not tag_id:
result.append(clockify_tag)
if len(tags) < page_size:
break
logger.debug(f"{Fore.YELLOW}Получено тегов: {len(tags)}.{Fore.RESET}")
return [Tag(
id=tag["id"],
title=tag["name"]
) for tag in tags]
page += 1
if tag_id:
return None
logger.debug(f"{Fore.YELLOW}Получено тегов: {len(result)}.{Fore.RESET}")
return result
return db.get_tags()
def get_vetro_projects(self) -> list[VetroProject]:

View File

View File

@@ -1,29 +0,0 @@
import logging
import asyncio
import sys
from bot.commands import router as command_router
from bot.factory import bot, dp, init_bot_and_dp
from config import config
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(name)s:%(lineno)d %(levelname)s %(message)s',
handlers=[
logging.FileHandler('app.log', encoding='utf-8'),
logging.StreamHandler(sys.stdout)
]
)
logger = logging.getLogger(__name__)
async def main():
bot, dp = init_bot_and_dp(f"socks5://{config.PROXY_URL}", config.BOT_TOKEN)
dp.include_router(command_router)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -1,30 +0,0 @@
from argparse import Namespace
from aiogram import Router
from aiogram.filters.command import Command, CommandStart
from aiogram.types import Message
from bot.factory import bot
from tracker.commands import end_workday
router = Router()
@router.message(CommandStart())
async def start_command(message: Message):
await message.answer(f"Привет, {message.from_user.first_name}!")
@router.message(Command("track"))
async def track_command(message: Message):
await message.answer("Эта фича пока в разработке, но скоро она поможет стартовать и останавливать таймер.")
@router.message(Command("end"))
async def end_command(message: Message):
args = message.text.replace("/end", "").strip().split()
if "-i" in args or "--init-db" in args:
args = Namespace(init_db=True)
await message.answer("Начинаю процесс занесения трудочасов...")
await end_workday(args, bot, message.from_user.id)
await message.answer("Трудочасы занесены!")

View File

@@ -1,12 +0,0 @@
from aiogram import Bot, Dispatcher
from aiogram.client.session.aiohttp import AiohttpSession
bot: Bot = None # type: ignore
dp: Dispatcher = None
def init_bot_and_dp(proxy_url: str, token: str):
global bot, dp
bot = Bot(token=token, session=AiohttpSession(proxy=proxy_url) if proxy_url else None)
dp = Dispatcher()
return bot, dp

View File

@@ -16,9 +16,6 @@ class Config(BaseSettings):
REDMINE_TOKEN: str
USER_ID: str = None
WORKSPACE_ID: str = None
BOT_TOKEN: str
PROXY_URL: str
MODE: str = "prod"

View File

@@ -2,7 +2,4 @@ REDMINE_TOKEN=
CLOCKIFY_TOKEN=
USER_ID=
WORKSPACE_ID=
TRIAL_END_DATE=
BOT_TOKEN=
PROXY_URL=
TRIAL_END_DATE=

18
main.py
View File

@@ -1,18 +0,0 @@
import asyncio
import logging
import sys
from tracker.commands import end_workday
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(name)s:%(lineno)d %(levelname)s %(message)s',
handlers=[
logging.FileHandler('app.log', encoding='utf-8'),
logging.StreamHandler(sys.stdout)
]
)
if __name__ == "__main__":
asyncio.run(end_workday())

View File

@@ -1,28 +1,15 @@
aiofiles==25.1.0
aiogram==3.27.0
aiohappyeyeballs==2.6.1
aiohttp==3.13.5
aiohttp_socks==0.11.0
aiosignal==1.4.0
annotated-types==0.7.0
asarPy==1.0.1
attrs==26.1.0
certifi==2025.4.26
charset-normalizer==3.4.2
colorama==0.4.6
frozenlist==1.8.0
idna==3.10
magic-filter==1.0.12
multidict==6.7.1
propcache==0.4.1
pydantic==2.11.4
pydantic-settings==2.9.1
pydantic_core==2.33.2
python-dotenv==1.1.0
python-socks==2.8.1
requests==2.32.3
tqdm==4.67.1
typing-inspection==0.4.0
typing_extensions==4.13.2
urllib3==2.4.0
yarl==1.23.0

View File

@@ -1,20 +1,24 @@
import logging
from argparse import Namespace
import sys
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(name)s:%(lineno)d %(levelname)s %(message)s',
handlers=[
logging.FileHandler('app.log', encoding='utf-8'),
logging.StreamHandler(sys.stdout)
]
)
from aiogram import Bot
from colorama import Fore # noqa: E402
from bot.factory import bot
from config import arg_parser, clock, config, db, red # noqa: E402
from utils import check_envs, db_update, format_date, get_start_end_dates # noqa: E402
async def end_workday(
args: Namespace = None,
bot: Bot = None,
user_id: int = None
):
args = args or arg_parser.parse_args()
def main():
args = arg_parser.parse_args()
if is_debug := args.debug:
config.MODE = "debug"
@@ -43,7 +47,7 @@ async def end_workday(
}
db.insert_user(**clock_user, **red_user)
if args.init_db:
await db_update(bot)
db_update()
activities = sorted(clock.get_activities(
**{
@@ -54,19 +58,16 @@ async def end_workday(
), key=lambda x: x.date_start)
if not activities:
logger.info(Fore.YELLOW + "Нет задач для занесения трудочасов" + Fore.RESET)
if bot:
await bot.send_message(user_id, "Нет задач для занесения трудочасов")
return
db.insert_activities(activities)
try:
tracked_activities = red.track_activities(activities)
if bot:
await bot.send_message(user_id, f"Трудочасы занесены: {len(tracked_activities)}/{len(activities)}.")
clock.mark_tracked(tracked_activities)
if not is_debug:
db.insert_time_entries(tracked_activities)
db.insert_activities(tracked_activities)
except Exception as e:
if bot:
await bot.send_message(user_id, f"Ошибка при трекинге трудочасов: {e}")
logger.error(e)
if __name__ == "__main__":
main()

View File

@@ -5,7 +5,6 @@ import os
import re
from typing import Optional
from aiogram import Bot
from colorama import Fore
from config import config
@@ -41,11 +40,9 @@ def format_date(date: datetime) -> datetime:
def parse_task_id(tag: str) -> int:
return int(re.search(r"^(?P<task_id>\d{5})", tag).group("task_id"))
async def db_update(bot: Bot = None, user_id: int = None):
def db_update():
from config import clock, db, red
logger.info(Fore.YELLOW + "Обновление базы данных..." + Fore.RESET)
if bot:
await bot.send_message(user_id, "Обновление базы данных...")
try:
# Обновление Тегов