Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
altlinux-packages-bot
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kirill Unitsaev
altlinux-packages-bot
Commits
a333db40
Verified
Commit
a333db40
authored
Mar 10, 2026
by
Kirill Unitsaev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
news: add /pulse command for repository statistics summary
parent
48947111
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
79 additions
and
3 deletions
+79
-3
methods.py
src/altrepo/parser/methods.py
+26
-0
urls.py
src/altrepo/parser/news/urls.py
+5
-2
help.py
src/handlers/help.py
+1
-0
news.py
src/handlers/news.py
+47
-1
No files found.
src/altrepo/parser/methods.py
View file @
a333db40
...
...
@@ -54,6 +54,32 @@ class NewsInfo:
async
def
p10
(
self
)
->
models
.
PackagesModel
|
None
:
return
await
self
.
_get_packages
(
"p10"
)
async
def
bugs_by_range
(
self
,
date_from
:
date
,
date_to
:
date
)
->
dict
[
str
,
int
]
|
None
:
urls
=
await
urls_for_range
(
self
.
client
,
date_from
,
date_to
,
"bugs"
)
if
not
urls
:
return
None
totals
=
{
"quickly_resolved"
:
0
,
"new"
:
0
,
"old"
:
0
,
"resolved"
:
0
,
"reopened"
:
0
,
"random"
:
0
,
}
for
_
,
url
in
urls
:
html
=
await
self
.
client
.
get
(
url
,
"koi8-r"
)
data
=
await
bugs_parser
(
html
,
url
)
if
data
and
isinstance
(
data
,
models
.
BugsModel
):
for
key
in
totals
:
items
=
getattr
(
data
,
key
,
None
)
if
items
:
totals
[
key
]
+=
len
(
items
)
if
all
(
v
==
0
for
v
in
totals
.
values
()):
return
None
return
totals
async
def
packages_by_range
(
self
,
date_from
:
date
,
date_to
:
date
)
->
models
.
PackagesModel
|
None
:
...
...
src/altrepo/parser/news/urls.py
View file @
a333db40
...
...
@@ -41,8 +41,11 @@ async def urls_parser(client):
return
models
.
NewsURL
(
**
result
)
async
def
urls_for_range
(
client
,
date_from
:
date
,
date_to
:
date
)
->
list
[
tuple
[
date
,
str
]]:
async
def
urls_for_range
(
client
,
date_from
:
date
,
date_to
:
date
,
news_type
:
str
=
"packages"
)
->
list
[
tuple
[
date
,
str
]]:
results
=
[]
pattern
=
re
.
compile
(
rf
"Sisyphus-(
\
d{{8}}) {re.escape(news_type)}"
)
current
=
date_from
.
replace
(
day
=
1
)
while
current
<=
date_to
:
...
...
@@ -61,7 +64,7 @@ async def urls_for_range(client, date_from: date, date_to: date) -> list[tuple[d
if
not
a
or
not
a
.
get
(
"href"
):
continue
text
=
a
.
get_text
(
strip
=
True
)
match
=
re
.
search
(
r"Sisyphus-(\d{8}) packages"
,
text
)
match
=
pattern
.
search
(
text
)
if
match
:
news_date
=
datetime
.
strptime
(
match
.
group
(
1
),
"
%
Y
%
m
%
d"
)
.
date
()
if
date_from
<=
news_date
<=
date_to
:
...
...
src/handlers/help.py
View file @
a333db40
...
...
@@ -76,5 +76,6 @@ async def help_handler(m: Message) -> None:
" /statistics [branch] — статистика репозитория
\n
"
" /news — меню новостей
\n
"
" /news_range дата_от дата_до — новости за период
\n
"
" /pulse [дней] — пульс Сизифа (по умолчанию 7)
\n
"
)
src/handlers/news.py
View file @
a333db40
import
asyncio
from
telegrinder
import
Dispatch
,
Message
,
CallbackQuery
from
telegrinder.rules
import
Command
,
Argument
,
CallbackDataMarkup
,
Text
,
IsPrivate
from
datetime
import
datetime
from
datetime
import
datetime
,
timedelta
from
altrepo
import
altrepo
from
services.news
import
format_packages
,
format_bugs
from
services.utils
import
_bold
,
int_validator
from
data.keyboards
import
news_keyboards
dp
=
Dispatch
()
...
...
@@ -164,3 +167,46 @@ async def news_range_handler(
for
msg
in
updated
:
await
m
.
ctx_api
.
send_message
(
chat_id
=
chat_id
,
text
=
msg
)
await
m
.
ctx_api
.
send_message
(
chat_id
=
chat_id
,
text
=
info_message
)
@dp.message
(
Command
(
"pulse"
,
Argument
(
"days"
,
[
int_validator
],
optional
=
True
))
)
async
def
pulse_handler
(
m
:
Message
,
days
:
int
|
None
=
None
)
->
None
:
days
=
days
or
7
if
days
<
1
or
days
>
180
:
await
m
.
answer
(
"Допустимый диапазон: 1–180 дней."
)
return
d_to
=
datetime
.
now
()
.
date
()
d_from
=
d_to
-
timedelta
(
days
=
days
-
1
)
await
m
.
answer
(
f
"Сбор статистики за {days} дн..."
)
packages_data
,
bugs_data
=
await
asyncio
.
gather
(
altrepo
.
parser
.
news
.
packages_by_range
(
d_from
,
d_to
),
altrepo
.
parser
.
news
.
bugs_by_range
(
d_from
,
d_to
),
)
added
=
len
(
packages_data
.
added
)
if
packages_data
and
packages_data
.
added
else
0
updated
=
len
(
packages_data
.
updated
)
if
packages_data
and
packages_data
.
updated
else
0
removed
=
len
(
packages_data
.
removed
)
if
packages_data
and
packages_data
.
removed
else
0
message
=
_bold
(
"Пульс Сизифа
\n\n
"
)
message
+=
"🛍 Пакеты:
\n
"
message
+=
f
" Добавлено новых: {added}
\n
"
message
+=
f
" Обновлено: {updated}
\n
"
message
+=
f
" Удалено: {removed}
\n\n
"
if
bugs_data
:
message
+=
"🪲 Баги:
\n
"
message
+=
f
" Новые: {bugs_data['new']}
\n
"
message
+=
f
" Переоткрытые: {bugs_data['reopened']}
\n
"
message
+=
f
" Быстро закрытые: {bugs_data['quickly_resolved']}
\n
"
message
+=
f
" Закрытые: {bugs_data['resolved']}
\n\n
"
date_range
=
f
"{d_from.strftime('
%
d.
%
m.
%
Y')} — {d_to.strftime('
%
d.
%
m.
%
Y')}"
message
+=
f
"* статистика за {date_range}"
await
m
.
answer
(
message
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment