-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathupdate.py
More file actions
268 lines (226 loc) · 10.2 KB
/
update.py
File metadata and controls
268 lines (226 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
from datetime import datetime, timedelta
import feedparser
import requests
import base64
import json
import hashlib
import re
from cn_helpers import utc8_now, parse_cn_time, cn_derived_status
# 国外赛事更新:
rssUpcoming = 'https://ctftime.org/event/list/upcoming/rss/'
rssActive = 'https://ctftime.org/event/list/archive/rss/'
rssNowrunning = 'https://ctftime.org/event/list/running/rss/'
def fetch_global_ctf_content(rss_url):
try:
feed = feedparser.parse(rss_url)
except Exception as e:
print(f'RSS源获取失败: {e}')
return False
events = []
# 检查feed是否有效
if not feed or 'feed' not in feed or 'title' not in feed.get('feed', {}):
print('RSS源解析失败,请检查URL是否正确')
return False
for entry in feed.entries:
try:
eventName = getattr(entry, 'title', 'Unknown Event')
# 时间处理部分
start_date_str = getattr(entry, 'start_date', None)
finish_date_str = getattr(entry, 'finish_date', None)
if not start_date_str or not finish_date_str:
print(f'Warning: Event "{eventName}" missing date info, skipping')
continue
start_date = datetime.strptime(start_date_str, '%Y%m%dT%H%M%S')
finish_date = datetime.strptime(finish_date_str, '%Y%m%dT%H%M%S')
start_date_utc8 = start_date + timedelta(hours=8)
finish_date_utc8 = finish_date + timedelta(hours=8)
time_range = f'{start_date_utc8.strftime("%Y-%m-%d %H:%M:%S")} - {finish_date_utc8.strftime("%Y-%m-%d %H:%M:%S")} UTC+8'
eventTime = time_range
# 添加日历部分
description = getattr(entry, 'description', '')
addCalendar = None
calendar_start_index = description.find('[add to calendar]')
if calendar_start_index != -1:
calendar_end_index = description.find('</a>', calendar_start_index)
calendar_link_start_index = description.rfind('href="', 0, calendar_start_index) + 6
calendar_link = description[calendar_link_start_index:calendar_end_index]
calendar_link = calendar_link.replace('">[add to calendar]','')
addCalendar = calendar_link
# 主办方部分
organizers_str = getattr(entry, 'organizers', '[]')
try:
organizers_data = json.loads(organizers_str)
except json.JSONDecodeError:
organizers_data = []
organizers_names = []
organizers_urls = []
for organizer in organizers_data:
name = organizer.get('name', 'Unknown')
org_id = organizer.get('id', '')
url = f'https://ctftime.org/team/{org_id}' if org_id else ''
organizers_names.append(name)
organizers_urls.append(url)
organizers_names_str = ', '.join(organizers_names)
organizers_urls_str = ', '.join(organizers_urls)
eventUrl = getattr(entry, 'url', '')
eventType = getattr(entry, 'format_text', '')
logo_url = getattr(entry, 'logo_url', '')
eventLogo = f'https://ctftime.org{logo_url}' if logo_url else ''
eventWeight = getattr(entry, 'weight', '0.00')
eventOrganizers = f'{organizers_names_str} ({organizers_urls_str})' if organizers_names_str else ''
eventData = {
'比赛名称': eventName,
'比赛时间': eventTime,
'添加日历': addCalendar,
'比赛形式': eventType,
'比赛链接': eventUrl,
'比赛标志': eventLogo,
'比赛权重': eventWeight,
'赛事主办': eventOrganizers,
'比赛ID': getattr(entry, 'ctf_id', ''),
'比赛状态': ''
}
if rss_url == rssUpcoming:
eventData['比赛状态'] = 'oncoming'
elif rss_url == rssActive:
eventData['比赛状态'] = 'past'
elif rss_url == rssNowrunning:
eventData['比赛状态'] = 'nowrunning'
events.append(eventData)
except Exception as e:
print(f'Warning: Failed to parse entry "{getattr(entry, "title", "Unknown")}": {e}')
continue
return events
upcoming_events = fetch_global_ctf_content(rssUpcoming)
active_events = fetch_global_ctf_content(rssActive)
running_events = fetch_global_ctf_content(rssNowrunning)
all_events = None
if False not in [upcoming_events, active_events, running_events]:
all_events = upcoming_events + running_events + active_events
with open('Global.json', 'w', encoding='utf-8') as file:
json.dump(all_events, file, ensure_ascii=False, indent=4)
print("国际赛事数据已更新至Global.json")
else:
print("国际赛事数据更新失败")
# 国内赛事:归档已结束超过 60 天的比赛,并按状态与时间排序(状态由比赛时间推导,不写入 JSON)
with open('./CN.json', 'r', encoding='utf-8') as f:
CN = json.load(f)
date = utc8_now()
with open('Achieve/CN_archive.json', 'r', encoding='utf-8') as file:
archive = json.load(file)
to_archive = []
for event in CN['data']['result']:
comp_end = parse_cn_time(event['comp_time_end'])
if date > comp_end + timedelta(days=60):
print(event['name'] + "已结束超过60天,移至存档")
to_archive.append(event)
for event in to_archive:
archive['archive']['result'].append(event)
CN['data']['result'].remove(event)
status_order = {
'即将开始': 0,
'正在进行': 1,
'已经结束': 2,
}
CN['data']['result'] = sorted(
CN['data']['result'],
key=lambda x: (status_order.get(cn_derived_status(x), float('inf')), x['comp_time_start']),
)
CN['data']['total'] = len(CN['data']['result'])
with open('Achieve/CN_archive.json', 'w', encoding='utf-8') as file:
json.dump(archive, file, ensure_ascii=False, indent=4)
with open('./CN.json', 'w', encoding='utf-8') as f:
json.dump(CN, f, ensure_ascii=False, indent=4)
if all_events == None:
with open('Global.json', 'r', encoding='utf-8') as f:
all_events = json.load(f)
# 生成国内比赛的日历订阅内容
def create_CN_ical_event(event):
try:
start_date = datetime.strptime(event['comp_time_start'], '%Y年%m月%d日 %H:%M') - timedelta(hours=8)
finish_date = datetime.strptime(event['comp_time_end'], '%Y年%m月%d日 %H:%M') - timedelta(hours=8)
except (ValueError, TypeError, KeyError) as e:
print(f"Warning: Failed to create ical event for '{event.get('name', 'Unknown')}': {e}")
return None
detail = event.get('detail') or event.get('readmore', '')
desc = re.sub(r"\s+", "", str(detail))
eventData = {
'BEGIN': 'VEVENT',
'SUMMARY': event['name'],
'DTSTART': start_date.strftime("%Y%m%dT%H%M%SZ"),
'DTEND': finish_date.strftime("%Y%m%dT%H%M%SZ"),
'UID': hashlib.md5(event['name'].encode('utf-8')).hexdigest(),
'VTIMEZONE': 'Asia/Shanghai',
'DTSTAMP': datetime.now().strftime("%Y%m%dT%H%M%SZ"),
'CREATED': datetime.now().strftime("%Y%m%dT%H%M%SZ"),
'URL': event['link'],
'DESCRIPTION': event['link'] + ' | ' + desc,
'END': 'VEVENT'
}
return eventData
# 生成国外比赛的日历订阅内容
def create_Global_ical_event(event):
start_date = event['比赛时间'].split(' - ')[0]
finish_date = event['比赛时间'].split(' - ')[1].replace(' UTC+8', '')
start_date = datetime.strptime(start_date, '%Y-%m-%d %H:%M:%S') - timedelta(hours=8)
finish_date = datetime.strptime(finish_date, '%Y-%m-%d %H:%M:%S') - timedelta(hours=8)
eventData= {
'BEGIN':'VEVENT',
'SUMMARY':event['比赛名称'],
'DTSTART':start_date.strftime("%Y%m%dT%H%M%SZ"),
'DTEND':finish_date.strftime("%Y%m%dT%H%M%SZ"),
'UID':hashlib.md5(event['比赛名称'].encode('utf-8')).hexdigest(),
'VTIMEZONE':'Asia/Shanghai',
'DTSTAMP':datetime.now().strftime("%Y%m%dT%H%M%SZ"),
'CREATED':datetime.now().strftime("%Y%m%dT%H%M%SZ"),
'URL':event['比赛链接'],
'DESCRIPTION':event['比赛形式']+' | '+event['比赛链接']+' | '+' | '+'比赛ID - '+str(event['比赛ID']),
'END':'VEVENT'
}
return eventData
# 生成国内赛事日历
CN_ical_events = []
for event in CN['data']['result']:
ical_event = create_CN_ical_event(event)
if ical_event:
CN_ical_events.append(ical_event)
with open('./calendar/CN.ics', 'w', encoding='utf-8') as f:
f.write('BEGIN:VCALENDAR\n')
f.write('VERSION:2.0\n')
f.write('PRODID:-//CTF//CN//\n')
f.write('CALSCALE:GREGORIAN\n')
f.write('X-WR-CALNAME:CN\n')
for event in CN_ical_events:
for key, value in event.items():
f.write(f'{key}:{value}\n')
f.write('\n')
f.write('END:VCALENDAR')
# 生成国际赛事日历
Global_ical_events = []
for event in all_events:
Global_ical_events.append(create_Global_ical_event(event))
with open('./calendar/Global.ics', 'w', encoding='utf-8') as f:
f.write('BEGIN:VCALENDAR\n')
f.write('TZID:Asia/Shanghai\n')
f.write('VERSION:2.0\n')
f.write('PRODID:-//CTF//Global//\n')
f.write('CALSCALE:GREGORIAN\n')
f.write('X-WR-CALNAME:Global\n')
for event in Global_ical_events:
for key, value in event.items():
f.write(f'{key}:{value}\n')
f.write('\n')
f.write('END:VCALENDAR')
# 将Global.json 和 CN.json 通过Base64编码后存储为 Global.b64 以供大陆用户使用
with open('Global.json', 'r', encoding='utf-8') as f:
Global = f.read()
Global = Global.encode('utf-8')
Global = base64.b64encode(Global).decode('utf-8')
with open('Global.b64', 'w', encoding='utf-8') as f:
f.write(Global)
with open('CN.json', 'r', encoding='utf-8') as f:
CN = f.read()
CN = CN.encode('utf-8')
CN = base64.b64encode(CN).decode('utf-8')
with open('CN.b64', 'w', encoding='utf-8') as f:
f.write(CN)