Skip to content

Commit 94f6762

Browse files
authored
Merge pull request #5 from UmmItKin/master
Add CI Workflow and Enhance Development Environment
2 parents b75b59b + 6cc9708 commit 94f6762

File tree

10 files changed

+543
-105
lines changed

10 files changed

+543
-105
lines changed

.github/workflows/ci.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
pull_request:
7+
branches: [ main, master ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.13"]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Install uv
20+
uses: astral-sh/setup-uv@v3
21+
with:
22+
enable-cache: true
23+
cache-dependency-glob: "uv.lock"
24+
25+
- name: Set up Python ${{ matrix.python-version }}
26+
run: uv python install ${{ matrix.python-version }}
27+
28+
- name: Install dependencies
29+
run: uv sync
30+
31+
- name: Lint with ruff (if available)
32+
run: |
33+
if uv run --quiet ruff --version 2>/dev/null; then
34+
uv run ruff check .
35+
else
36+
echo "Ruff not installed, skipping linting"
37+
fi
38+
continue-on-error: true
39+
40+
- name: Check code formatting with ruff (if available)
41+
run: |
42+
if uv run --quiet ruff --version 2>/dev/null; then
43+
uv run ruff format --check
44+
else
45+
echo "Ruff not installed, skipping format check"
46+
fi
47+
continue-on-error: true
48+
49+
security-check:
50+
runs-on: ubuntu-latest
51+
steps:
52+
- uses: actions/checkout@v4
53+
54+
- name: Install uv
55+
uses: astral-sh/setup-uv@v3
56+
57+
- name: Set up Python
58+
run: uv python install 3.13
59+
60+
- name: Install dependencies
61+
run: uv sync
62+
63+
- name: Security check with bandit (if available)
64+
run: |
65+
if uv run --quiet bandit --version 2>/dev/null; then
66+
uv run bandit -r . -f json || true
67+
else
68+
echo "Bandit not installed, skipping security check"
69+
fi
70+
continue-on-error: true

ctfeed.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,43 @@
1111
from src.embed_creator import create_event_embed
1212

1313
logging.basicConfig(level=logging.INFO)
14-
logging.getLogger('discord.client').setLevel(logging.ERROR)
14+
logging.getLogger("discord.client").setLevel(logging.ERROR)
1515
logger = logging.getLogger(__name__)
1616

1717
bot = discord.Client(intents=discord.Intents.default())
1818

1919
known_events = load_known_events()
2020

21+
2122
@bot.event
2223
async def on_ready():
23-
logger.info(f'Bot logged in: {bot.user}')
24+
logger.info(f"Bot logged in: {bot.user}")
2425
if known_events:
25-
logger.info(f"Loading data/known_events.json for {len(known_events)} known competitions")
26+
logger.info(
27+
f"Loading data/known_events.json for {len(known_events)} known competitions"
28+
)
2629
else:
27-
logger.info("No known competitions found, creating new file data/known_events.json")
30+
logger.info(
31+
"No known competitions found, creating new file data/known_events.json"
32+
)
2833
save_known_events(set())
2934
check_new_events.start()
3035

36+
3137
@tasks.loop(minutes=CHECK_INTERVAL)
3238
async def check_new_events():
3339
global known_events
34-
40+
3541
events = await fetch_ctf_events()
36-
42+
3743
channel_name = ANNOUNCEMENT_CHANNEL_ID
3844
if not channel_name:
3945
logger.error("Please set ANNOUNCEMENT_CHANNEL_ID in .env file")
4046
logger.error("For example: ANNOUNCEMENT_CHANNEL_ID=ctftime")
4147
logger.error("Please check if the .env file is correctly set")
4248
await bot.close()
4349
return
44-
50+
4551
channel = None
4652
for guild in bot.guilds:
4753
for text_channel in guild.text_channels:
@@ -50,7 +56,7 @@ async def check_new_events():
5056
break
5157
if channel:
5258
break
53-
59+
5460
if not channel:
5561
logger.error(f"Can't find channel named '{channel_name}'")
5662
logger.error(f"Please check:")
@@ -59,10 +65,10 @@ async def check_new_events():
5965
logger.error(f"3. The channel exists in the server where the Bot is located")
6066
await bot.close()
6167
return
62-
68+
6369
new_events_found = False
6470
for event in events:
65-
event_id = event['id']
71+
event_id = event["id"]
6672
if event_id not in known_events:
6773
known_events.add(event_id)
6874
new_events_found = True
@@ -73,21 +79,24 @@ async def check_new_events():
7379
logger.info(f"Sent new event notification: {event['title']}")
7480
except Exception as e:
7581
logger.error(f"Failed to send notification: {e}")
76-
82+
7783
if new_events_found:
7884
save_known_events(known_events)
7985

86+
8087
@check_new_events.before_loop
8188
async def before_check():
8289
await bot.wait_until_ready()
8390

91+
8492
def main():
8593
if not DISCORD_BOT_TOKEN:
8694
print("Please set DISCORD_BOT_TOKEN in .env file")
8795
exit(1)
88-
96+
8997
print("Start CTF Bot...")
9098
bot.run(DISCORD_BOT_TOKEN)
9199

100+
92101
if __name__ == "__main__":
93-
main()
102+
main()

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ dependencies = [
1212
"pytz>=2023.3"
1313
]
1414

15+
[dependency-groups]
16+
dev = [
17+
"ruff>=0.1.0",
18+
"mypy>=1.0.0",
19+
"bandit>=1.7.0",
20+
]
21+
1522
[build-system]
1623
requires = ["hatchling"]
1724
build-backend = "hatchling.build"

ruff.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[lint]
2+
select = [
3+
"E", # pycodestyle errors
4+
"W", # pycodestyle warnings
5+
"F", # pyflakes
6+
"I", # isort
7+
"B", # flake8-bugbear
8+
"C4", # flake8-comprehensions
9+
"UP", # pyupgrade
10+
]
11+
12+
ignore = [
13+
"E501", # line too long, handled by formatter
14+
]
15+
16+
[format]
17+
quote-style = "double"
18+
indent-style = "space"
19+
skip-magic-trailing-comma = false
20+
line-ending = "auto"
21+
22+
[lint.per-file-ignores]
23+
"__init__.py" = ["F401"] # Allow unused imports in __init__.py files

src/config.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
load_dotenv()
55

6-
DISCORD_BOT_TOKEN = os.getenv('DISCORD_BOT_TOKEN')
7-
ANNOUNCEMENT_CHANNEL_ID = os.getenv('ANNOUNCEMENT_CHANNEL_ID')
8-
KNOWN_EVENTS_FILE = os.getenv('KNOWN_EVENTS_FILE', 'data/known_events.json')
6+
DISCORD_BOT_TOKEN = os.getenv("DISCORD_BOT_TOKEN")
7+
ANNOUNCEMENT_CHANNEL_ID = os.getenv("ANNOUNCEMENT_CHANNEL_ID")
8+
KNOWN_EVENTS_FILE = os.getenv("KNOWN_EVENTS_FILE", "data/known_events.json")
99

1010
CTFTIME_API_URL = "https://ctftime.org/api/v1/events/"
1111
TEAM_API_URL = "https://ctftime.org/api/v1/teams/"
1212

1313
CHECK_INTERVAL = 30
1414

15-
SEARCH_DAYS = 90
15+
SEARCH_DAYS = 90

0 commit comments

Comments
 (0)