<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      自動拉取各大OJ的比賽日程并導入日歷軟件

      參考文章:使用日歷app輕松訂閱各大OJ平臺上的比賽(ics格式)

      tips:代碼為Grok3生成,能跑。

      過去看比賽日期的方式都弱爆了!需要自己手動打開各OJ的網頁,有時忘看了還會錯過比賽,而現在我們再也不需要擔心這個問題了。

      預覽

      手機預覽
      電腦預覽

      正文

      快速開始

      如果你不想自己部署,可以使用我的訂閱鏈接,但維護有成本(SCF云函數,域名,對象存儲等),我或許不會維護很久。

      Win電腦與Android手機

      Outlook注冊一個Outlook賬號,如果已經有就登錄,手機用戶需要下載Outlook軟件。

      登入后,在左邊點擊日歷按鈕,點擊左側的“添加日歷”,點擊“Web訂閱”,填入我的訂閱鏈接:
      https://oss.misaka2298.icu/oss/calendar.ics

      點擊導入,電腦端的操作到這里就結束了,注意上方的可視范圍從默認的"工作周"切換為"周",不然看不到周末的比賽。

      手機端需要多一步操作:在Outlook的設置里找到“日歷”設置,勾選“同步日歷”,并同意Outlook申請的日歷訪問權限,然后手機日歷就會自動同步了。

      iPhone

      我手頭沒有iPhone機器,這里引用我參考文章里的步驟:

      • 打開ios日歷,點擊添加日歷 - 添加訂閱日歷
      • 粘貼鏈接
      • 進行自定義設置,完成

      鏈接同https://oss.misaka2298.icu/oss/calendar.ics

      自己部署

      可能產生的費用

      騰訊云SCF函數:個人標準版函數套餐12.8元/月。

      域名:冷門頂級域名(如我的.icu)約100/年,首年優惠。當然也可以選用網上公益的二級域名服務,請自行搜索。

      對象存儲:

      • CloudFlareR2(本文使用):基本免費,但需要一張銀行卡(支持銀聯)
      • 其他對象存儲:按量收費,如果訪問量大可能產生較高的費用。

      如果可以接受的話,下面是教程。

      環境安裝

      首先,需要Python3.9的環境(截止到本文發布),安裝時記得勾選"Add to PATH",安裝后重啟。

      找個空文件夾,打開cmd,執行下面的命令:

      mkdir layer
      cd layer
      python -m pip install requests==2.28.2 beautifulsoup4==4.12.3 ics==0.7.1 boto3==1.34.149 urllib3==1.26.18 tatsu==5.7.4 -t .
      

      把layer文件夾壓縮成zip,備用。

      對象存儲

      打開CloudFlare控制臺,沒有賬號就注冊一個,在左側選項卡找到R2對象存儲,按提示初始化,注意需要銀行卡。

      當然如果你要用其他對象存儲服務商的話可以自行研究。

      點擊{}API,點擊管理API令牌,然后創建UserAPI令牌,權限為管理員讀寫,名稱自己取,然后記住你的訪問密鑰 ID機密訪問密鑰,注意這兩個東西只會出現一次。

      返回R2控制臺,創建新的存儲桶,名稱自己起,位置選亞太,除非你在外國。

      進入存儲桶,在設置中添加自定義域,這里不再贅述,網上也有很多公益的二級域名供使用,請自行搜索教程。

      SCF自動拉取

      打開騰訊云SCF控制臺,沒注冊的話注冊一個。

      點擊左側“函數服務”,點擊新建。

      點擊"從頭開始",函數類型選事件函數,名稱自己起,地域無所謂,運行環境python3.9,時區UTC。

      在下方粘貼我的代碼:

      import json
      import requests
      import re
      import datetime
      import urllib
      from bs4 import BeautifulSoup
      import ics
      import boto3
      from botocore.client import Config
      import os
      import time
      
      R2_ACCESS_KEY = os.environ.get('R2_ACCESS_KEY', '')
      R2_SECRET_KEY = os.environ.get('R2_SECRET_KEY', '')
      R2_ENDPOINT_URL = os.environ.get('R2_ENDPOINT_URL', '')
      R2_BUCKET_NAME = os.environ.get('R2_BUCKET_NAME', '')
      R2_OBJECT_NAME = os.environ.get('R2_OBJECT_NAME', 'calendar.ics')
      
      def get_luogu_contests():
          headers = {
              'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE'
          }
          try:
              start_time = time.time()
              res = requests.get('https://www.luogu.com.cn/contest/list', headers=headers, timeout=5)
              print(f"洛谷請求耗時 {time.time() - start_time:.2f} 秒")
              mat = re.findall(r'JSON\.parse\(decodeURIComponent\("(\S+)"\)', res.text)[0]
              mat = json.loads(urllib.parse.unquote(mat))
              contests = []
              for t in mat['currentData']['contests']['result']:
                  title = t['name']
                  start_time = datetime.datetime.fromtimestamp(t['startTime'])
                  end_time = datetime.datetime.fromtimestamp(t['endTime'])
                  start_time = start_time + datetime.timedelta(hours=-8)
                  end_time = end_time + datetime.timedelta(hours=-8)
                  contests.append({"title": title, "start": start_time, "end": end_time})
              return contests
          except Exception as e:
              print(f"洛谷抓取失敗: {e}")
              return []
      
      def get_atcoder_contests():
          try:
              start_time = time.time()
              url = "https://atcoder.jp/contests/"
              res = requests.get(url, timeout=5)
              print(f"AtCoder 請求耗時 {time.time() - start_time:.2f} 秒")
              soup = BeautifulSoup(res.text, 'html.parser')
              contests = []
              for row in soup.select('#contest-table-upcoming tbody tr'):
                  cols = row.find_all('td')
                  start_time = datetime.datetime.strptime(cols[0].text, '%Y-%m-%d %H:%M:%S%z')
                  title = cols[1].text.strip()
                  title = re.sub(r'[^\w\s\-\(\)]', '', title).strip()
                  duration = cols[2].text.strip()
                  hours, minutes = map(int, duration.split(':'))
                  end_time = start_time + datetime.timedelta(hours=hours, minutes=minutes)
                  contests.append({"title": title, "start": start_time, "end": end_time})
              return contests
          except Exception as e:
              print(f"AtCoder 抓取失敗: {e}")
              return []
      
      def get_codeforces_contests():
          try:
              start_time = time.time()
              url = "https://codeforces.com/api/contest.list?gym=false"
              res = requests.get(url, timeout=5)
              print(f"Codeforces 請求耗時 {time.time() - start_time:.2f} 秒")
              data = res.json()
              contests = []
              for contest in data['result']:
                  if contest['phase'] == 'BEFORE':
                      title = contest['name']
                      start_time = datetime.datetime.fromtimestamp(contest['startTimeSeconds'], datetime.timezone.utc)
                      duration = contest['durationSeconds']
                      end_time = start_time + datetime.timedelta(seconds=duration)
                      contests.append({"title": title, "start": start_time, "end": end_time})
              return contests
          except Exception as e:
              print(f"Codeforces 抓取失敗: {e}")
              return []
      
      def main_handler(event, context):
          start_time = time.time()
          print("函數開始執行")
      
          registered = [
              get_luogu_contests(),
              get_atcoder_contests(),
              get_codeforces_contests()
          ]
          print(f"數據抓取總耗時 {time.time() - start_time:.2f} 秒")
      
          calendar = ics.Calendar()
          calendar_start = time.time()
          for dat in registered:
              if dat:
                  for res in dat:
                      print(res['title'], '|', res['start'], '|', res['end'])
                      e = ics.Event()
                      e.name = res['title']
                      e.begin = res['start']
                      e.end = res['end']
                      calendar.events.add(e)
          print(f"日歷創建耗時 {time.time() - calendar_start:.2f} 秒")
      
          ics_content = calendar.serialize()
      
          try:
              upload_start = time.time()
              s3 = boto3.client('s3',
                                endpoint_url=R2_ENDPOINT_URL,
                                aws_access_key_id=R2_ACCESS_KEY,
                                aws_secret_access_key=R2_SECRET_KEY,
                                config=Config(signature_version='s3v4'))
              s3.put_object(Bucket=R2_BUCKET_NAME, Key=R2_OBJECT_NAME, Body=ics_content, ContentType='text/calendar', ACL='public-read')
              print(f"ICS 文件上傳到 Cloudflare R2 成功,耗時 {time.time() - upload_start:.2f} 秒")
          except Exception as e:
              print(f"上傳到 R2 失敗: {e}")
              return {
                  'statusCode': 500,
                  'body': json.dumps({'error': str(e)})
              }
      
          print(f"總執行時間: {time.time() - start_time:.2f} 秒")
          return {
              'statusCode': 200,
              'body': json.dumps({'message': 'ICS 文件生成并上傳成功'})
          }
      
      if __name__ == '__main__':
          main_handler({}, {})
      

      在12~17行填寫你CloudFlare存儲桶的信息:

      • R2_ACCESS_KEY:APIKey的訪問密鑰 ID
      • R2_SECRET_KEY:APIKey的機密訪問密鑰
      • R2_ENDPOINT_URL:你的存儲桶 - 設置 - S3API的那一串鏈接
      • R2_BUCKET_NAME:存儲桶名
      • R2_OBJECT_NAME:保存的文件名,擴展名為.ics

      拉到最底下,在觸發器配置中勾選自定義創建,觸發別名/版本中選擇版本:$LATEST,觸發周期選擇每一天

      然后,在高級配置中把執行超時時間改為10秒。

      同意協議,點擊完成

      返回SCF控制臺,在左側進入的配置頁面。

      點擊新建,層名稱隨便寫,層代碼為你前面打包的layer.zip,運行環境添加Python3.9,點擊確定

      在左側進入函數服務的配置界面,進入你剛創建的函數,在上方進入層管理,點擊綁定,綁定你剛創建的

      點擊上方的函數代碼,點擊下方的測試,觀察執行摘要中的返回結果,如果一切順利,這里應該是

      {"statusCode": 200, "body": "{\"message\": \"ICS \文\件\生\成\并\上\傳\成\功\"}"}
      

      看看部署結果

      返回CloudFlareR2控制臺,進入存儲桶,尋找你生成的calender.ics

      如果存在的話,復制它的自定義域

      復制后在瀏覽器打開你復制的鏈接,如果可以下載就是成功了,導入教程同上。

      以后或許會實現的功能

      • 更多OJ的拉取
      • 在標題上標注是否Rated
      • 成本更低的部署
      • MacOS的導入(當然應該是支持的,可以自己摸索)
      posted @ 2025-07-24 15:39  Misaka2298  閱讀(66)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产成人午夜福利在线播放| 国产对白熟女受不了了| 中文字幕日韩精品亚洲一区 | 成人午夜电影福利免费| 国产精品中文字幕免费| 国产99在线 | 欧美| 国产三级精品三级在线看| 日韩av日韩av在线| 亚洲国产成人久久综合一区77| 亚洲欧美牲交| 亚洲精品www久久久久久| 国产av成人精品播放| 国产精品一区二区日韩精品 | 国产成人精品久久综合| 欧美成人精品手机在线| 国产人妻精品午夜福利免费| 欧洲极品少妇| 亚洲人成色99999在线观看| 国产又色又爽又黄的网站免费| 国产成人无码免费网站| 平武县| 国产口爆吞精在线视频2020版| 午夜精品福利亚洲国产| 无码人妻精品一区二区三区66| 亚洲一二区在线视频播放| 国产一区二区一卡二卡| www内射国产在线观看| 国内自拍小视频在线看| 国产成人无码免费视频在线| 日韩人妻无码一区二区三区久久| 亚洲第一香蕉视频啪啪爽| 极品少妇无套内射视频| 靖边县| 亚洲国产亚洲综合在线尤物| 亚洲αⅴ无码乱码在线观看性色| 美女自卫慰黄网站| 久久精品国产亚洲av高| 日韩av爽爽爽久久久久久| 国产jlzzjlzz视频免费看| 国产精品三级一区二区三区| 久久人人97超碰国产精品|