Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

**本项目为纯GitHub REST API实现,非webhook,不依赖相关服务**

其他语言 | Other languages: [English](https://github.com/HTony03/nonebot_plugin_github_release_notifier/README_en.md)
其他语言 | Other languages: [English](https://github.com/HTony03/nonebot_plugin_github_release_notifier/blob/main/README_en.md)

## 📖 介绍

Expand Down Expand Up @@ -60,19 +60,20 @@

在 nonebot2 项目的 `.env` 文件中添加下表中的必填配置:

| 配置项 | 必填 | 默认值 | 说明 |
|:-----:|:----:|:----:|:----:|
| GITHUB_TOKEN | 否 | 空字符串 | 用于访问 GitHub API 的 Token |
| GITHUB_RETRIES | 否 | 3 | 刷新最大重试次数 |
| GITHUB_RETRY_DELAY | 否 | 5 | 每次刷新重试之间的延迟(秒) |
| GITHUB_LANGUAGE | 否 | "en_us" | 发送的模板语言 |
| GITHUB_SEND_FALIURE_GROUP | 否 | True | 失败时是否通知群聊 |
| GITHUB_SEND_FALIURE_SUPERUSER | 否 | False | 失败时是否通知超级用户 |
| GITHUB_DEFAULT_CONFIG_SETTING | 否 | True | 添加仓库时默认监控所有事件 |
| GITHUB_SEND_IN_MARKDOWN | 否 | False | 是否以 Markdown 图片方式发送消息 |
| GITHUB_SEND_DETAIL_IN_MARKDOWN | 否 | True | 是否以 Markdown 图片方式发送详细信息(pr/issue/release)|
| GITHUB_UPLOAD_REMOVE_OLDER_VER | 否 | True | 上传 release 文件时是否移除旧版本( in development) |
| GITHUB_THEME | 否 | "dark" | (针对issue/pull request comment)渲染页面风格 ["light","dark"] |
| 配置项 | 必填 | 默认值 | 说明 |
|:-----:|:----:|:-------:|:-----------------------------------------------------:|
| GITHUB_TOKEN | 否 | 空字符串 | 用于访问 GitHub API 的 Token |
| GITHUB_RETRIES | 否 | 3 | 刷新最大重试次数 |
| GITHUB_RETRY_DELAY | 否 | 5 | 每次刷新重试之间的延迟(秒) |
| GITHUB_LANGUAGE | 否 | "en_us" | 发送的模板语言 |
| GITHUB_SEND_FALIURE_GROUP | 否 | True | 失败时是否通知群聊 |
| GITHUB_SEND_FALIURE_SUPERUSER | 否 | False | 失败时是否通知超级用户 |
| GITHUB_DEFAULT_CONFIG_SETTING | 否 | True | 添加仓库时默认监控所有事件 |
| GITHUB_SEND_IN_MARKDOWN | 否 | False | 是否以 Markdown 图片方式发送消息 |
| GITHUB_SEND_DETAIL_IN_MARKDOWN | 否 | True | 是否以 Markdown 图片方式发送详细信息(pr/issue/release) |
| GITHUB_SEND_PREV_DETAILS | 否 | False | 是否发送添加repo前的数据 |
| GITHUB_UPLOAD_REMOVE_OLDER_VER | 否 | True | 上传 release 文件时是否移除旧版本( in development) |
| GITHUB_THEME | 否 | "dark" | (针对issue/pull request comment)渲染页面风格 ["light","dark"] |

`v0.1.9` 删除了对于`.env`添加群组repo的适配, 请使用指令使用相关功能

Expand Down
27 changes: 14 additions & 13 deletions README_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,20 @@ Before use, ensure that the `SUPERUSERS` configuration item in NoneBot2 is set.

Add the following required configuration items to the `.env` file in your NoneBot2 project:

| Config Item | Required | Default | Description |
|:-----------:|:--------:|:-------:|:-----------:|
| GITHUB_TOKEN | No | Empty string | Token for accessing the GitHub API |
| GITHUB_RETRIES | No | 3 | Maximum retry attempts for refreshing |
| GITHUB_RETRY_DELAY | No | 5 | Delay between each refresh retry (seconds) |
| GITHUB_LANGUAGE | No | "en_us" | Language for sending templates |
| GITHUB_SEND_FALIURE_GROUP | No | True | Notify group on failure |
| GITHUB_SEND_FALIURE_SUPERUSER | No | False | Notify superuser on failure |
| GITHUB_DEFAULT_CONFIG_SETTING | No | True | Monitor all events by default when adding a repo |
| GITHUB_SEND_IN_MARKDOWN | No | False | Send messages as Markdown images |
| GITHUB_SEND_DETAIL_IN_MARKDOWN | No | True | Send details (pr/issue/release) as Markdown images |
| GITHUB_UPLOAD_REMOVE_OLDER_VER | No | True | Remove old versions when uploading release files (in development) |
| GITHUB_THEME | No | "dark" | (For issue/pull request comments) Page rendering style ["light","dark"] |
| Config Item | Required | Default | Description |
|:-----------:|:--------:|:-------:|:-----------------------------------------------------------------------:|
| GITHUB_TOKEN | No | Empty string | Token for accessing the GitHub API |
| GITHUB_RETRIES | No | 3 | Maximum retry attempts for refreshing |
| GITHUB_RETRY_DELAY | No | 5 | Delay between each refresh retry (seconds) |
| GITHUB_LANGUAGE | No | "en_us" | Language for sending templates |
| GITHUB_SEND_FALIURE_GROUP | No | True | Notify group on failure |
| GITHUB_SEND_FALIURE_SUPERUSER | No | False | Notify superuser on failure |
| GITHUB_DEFAULT_CONFIG_SETTING | No | True | Monitor all events by default when adding a repo |
| GITHUB_SEND_IN_MARKDOWN | No | False | Send messages as Markdown images |
| GITHUB_SEND_DETAIL_IN_MARKDOWN | No | True | Send details (pr/issue/release) as Markdown images |
| GITHUB_SEND_PREV_DETAILS | No | False | Whether send previous details when adding a new repository |
| GITHUB_UPLOAD_REMOVE_OLDER_VER | No | True | Remove old versions when uploading release files (in development) |
| GITHUB_THEME | No | "dark" | (For issue/pull request comments) Page rendering style ["light","dark"] |

`v0.1.9` removed support for adding group repo via `.env`. Please use commands for related features.

Expand Down
6 changes: 5 additions & 1 deletion src/nonebot_plugin_github_release_notifier/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ async def add_repo(
bot: Bot, event: GroupMessageEvent, args: Message = CommandArg()
):
"""Add a new repository mapping."""
from .repo_activity_new import github
from .repo_activity_new import github, initialize_repo_timestamps
command_args = args.extract_plain_text().split()
if len(command_args) < 1:
await bot.send(event, "Usage: repo add <repo> [group_id]")
Expand Down Expand Up @@ -123,6 +123,10 @@ async def fetch_repo(repo: str) -> Response[FullRepository]:
config.github_default_config_setting,
None,
False)
if not config.github_send_prev_details:
# Initialize timestamps to prevent flooding on first check
await initialize_repo_timestamps(repo)

await bot.send(event, f"Added repository mapping: {group_id} -> {repo}")
logger.info(f"Added repository mapping: {group_id} -> {repo}")

Expand Down
5 changes: 5 additions & 0 deletions src/nonebot_plugin_github_release_notifier/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ class Config(BaseModel): # pylint: disable=missing-class-docstring
- release
"""

github_send_prev_details: bool = False
"""
Whether send previous details when adding a new repository.
"""

github_comment_check_amount: int = 20
"""
The amount of issues/prs to check for comment each time when refresh.
Expand Down
8 changes: 4 additions & 4 deletions src/nonebot_plugin_github_release_notifier/db_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ def _create_tables(cursor: sqlite3.Cursor) -> None:
CREATE TABLE IF NOT EXISTS group_config (
group_id TEXT,
repo TEXT,
commits BOOLEAN,
issues BOOLEAN,
prs BOOLEAN,
releases BOOLEAN,
commits BOOLEAN DEFAULT FALSE,
issues BOOLEAN DEFAULT FALSE,
prs BOOLEAN DEFAULT FALSE,
releases BOOLEAN DEFAULT FALSE,
release_folder TEXT,
send_release BOOLEAN DEFAULT FALSE,
send_issue_comment BOOLEAN DEFAULT FALSE,
Expand Down
109 changes: 109 additions & 0 deletions src/nonebot_plugin_github_release_notifier/repo_activity_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,115 @@ async def get_data(
return response


async def initialize_repo_timestamps(repo: str) -> None:
"""
Initialize last_processed timestamps for a newly added repository.

This prevents flooding by marking all existing commits/issues/PRs/releases
as already processed, so only new items created after adding the repo
will trigger notifications.

The function performs two types of initialization:
1. Records timestamps of the latest items in the last_processed table
2. Records issue/PR IDs and latest comment IDs in the issues/prs tables
- Uses id=0 as a special key to track the most recent issue/PR itself
- Uses id=<issue_id> to track the most recent comment on that issue/PR

:param repo: GitHub repository in "owner/repo" format
:type repo: str
"""
last_processed = load_last_processed()

# Check if repo already has timestamps - if so, don't overwrite
if repo in last_processed:
logger.info(f"Repository {repo} already has timestamps, skipping initialization")
return

timestamps = {}
current_time = datetime.now(timezone.utc).isoformat()
owner, repository = repo.split('/')

# For each endpoint type, try to fetch the latest item and record its timestamp
for data_type, endpoint in [
("commit", "commits"),
("issue", "issues"),
("pull_req", "pulls"),
("release", "releases")
]:
try:
data = await fetch_github_data(repo, endpoint)
if data and len(data) > 0:
# Get the timestamp of the most recent item
item = data[0]
if isinstance(item, Commit):
timestamp = item.commit.author.date if item.commit.author else None
elif isinstance(item, (Issue, PullRequest, PullRequestSimple)):
timestamp = item.created_at

# For issues endpoint, initialize tracking for both issues and PRs
# to prevent comment flooding
if endpoint == "issues":
for content_type in ["issues", "prs"]:
try:
# Filter to get only issues or only PRs
filtered_items = [
i for i in data
if (content_type == "prs" and i.pull_request) or
(content_type == "issues" and not i.pull_request)
]

if filtered_items:
latest_filtered = filtered_items[0]
# Store the latest issue/PR ID in the database with special key id=0
# This tracks when a completely new issue/PR is created
save_commit_data(repo, str(latest_filtered.id), 0, content_type)

# Also store latest comment ID for this issue/PR if comments exist
try:
comments_resp = await github.rest.issues.async_list_comments(
owner=owner,
repo=repository,
issue_number=latest_filtered.number
)
comments = comments_resp.parsed_data
if comments:
latest_comment = comments[-1]
save_commit_data(repo, str(latest_comment.id), latest_filtered.id, content_type)
except Exception as e:
logger.debug(
f"Could not fetch comments for {content_type[:-1]} #{latest_filtered.number} "
f"during initialization: {e.__class__.__name__}"
)
except Exception as e:
logger.warning(
f"Failed to initialize {content_type} tracking for {repo}: "
f"{e.__class__.__name__}: {e}"
)

elif isinstance(item, Release):
timestamp = item.published_at
else:
timestamp = None

timestamps[data_type] = timestamp.isoformat() if timestamp else current_time
else:
# No items exist, use current time
timestamps[data_type] = current_time
except Exception as e:
logger.warning(
f"Failed to fetch {endpoint} for {repo} during initialization: "
f"{e.__class__.__name__}: {e}. Using current time."
)
timestamps[data_type] = current_time

# Save the timestamps
last_processed[repo] = timestamps
save_last_processed(last_processed)
logger.info(f"Initialized timestamps for repository {repo}")




async def process_issues_and_prs(
repo: str,
owner: str,
Expand Down
Loading