izuemon commited on
Commit
c382cb0
·
verified ·
1 Parent(s): 8694076

Create news.py

Browse files
Files changed (1) hide show
  1. news.py +139 -0
news.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import json
4
+ import requests
5
+ import hashlib
6
+ from datetime import datetime, timezone
7
+ from bs4 import BeautifulSoup
8
+ import xml.etree.ElementTree as ET
9
+
10
+ # ===== Channel.io 設定 =====
11
+ CHANNEL_ID = "200605"
12
+ GROUP_ID = "534457"
13
+
14
+ POST_URL = f"https://desk-api.channel.io/desk/channels/{CHANNEL_ID}/groups/{GROUP_ID}/messages"
15
+
16
+ X_ACCOUNT = os.getenv("channeliotokenbot2")
17
+ if not X_ACCOUNT:
18
+ raise RuntimeError("環境変数 channeliotokenbot2 が設定されていません")
19
+
20
+ HEADERS_POST = {
21
+ "accept": "application/json",
22
+ "accept-language": "ja",
23
+ "content-type": "application/json",
24
+ "x-account": X_ACCOUNT,
25
+ }
26
+
27
+ # ===== RSS =====
28
+ RSS_URL = "https://www.nippon.com/ja/rss-all/"
29
+
30
+ # ===== 設定 =====
31
+ INTERVAL_SECONDS = 60 * 60 * 12 # 12時間
32
+ SENT_LOG_FILE = "sent_nippon_news.json"
33
+
34
+
35
+ # ===== Utils =====
36
+ def load_sent_log():
37
+ if not os.path.exists(SENT_LOG_FILE):
38
+ return set()
39
+ with open(SENT_LOG_FILE, "r", encoding="utf-8") as f:
40
+ return set(json.load(f))
41
+
42
+
43
+ def save_sent_log(sent_set):
44
+ with open(SENT_LOG_FILE, "w", encoding="utf-8") as f:
45
+ json.dump(list(sent_set), f, ensure_ascii=False, indent=2)
46
+
47
+
48
+ def hash_link(link: str) -> str:
49
+ """リンクをハッシュ化(重複防止用)"""
50
+ return hashlib.sha256(link.encode("utf-8")).hexdigest()
51
+
52
+
53
+ def send_to_channel(text):
54
+ payload = {
55
+ "requestId": f"desk-web-{int(time.time() * 1000)}",
56
+ "blocks": [
57
+ {"type": "text", "value": text}
58
+ ],
59
+ }
60
+
61
+ res = requests.post(
62
+ POST_URL,
63
+ headers=HEADERS_POST,
64
+ data=json.dumps(payload),
65
+ timeout=30
66
+ )
67
+ res.raise_for_status()
68
+
69
+
70
+ def fetch_rss_items():
71
+ res = requests.get(RSS_URL, timeout=30)
72
+ res.raise_for_status()
73
+
74
+ root = ET.fromstring(res.content)
75
+ channel = root.find("channel")
76
+ if channel is None:
77
+ return []
78
+
79
+ items = []
80
+ for item in channel.findall("item"):
81
+ title = item.findtext("title", "").strip()
82
+ link = item.findtext("link", "").strip()
83
+ description_raw = item.findtext("description", "").strip()
84
+
85
+ # CDATA 内のHTMLをテキスト化
86
+ soup = BeautifulSoup(description_raw, "lxml")
87
+ description = soup.get_text(strip=True)
88
+
89
+ if title and link:
90
+ items.append({
91
+ "title": title,
92
+ "link": link,
93
+ "description": description,
94
+ })
95
+
96
+ return items
97
+
98
+
99
+ # ===== Main =====
100
+ def main():
101
+ sent_log = load_sent_log()
102
+
103
+ while True:
104
+ try:
105
+ items = fetch_rss_items()
106
+ new_count = 0
107
+
108
+ for item in items:
109
+ link_hash = hash_link(item["link"])
110
+ if link_hash in sent_log:
111
+ continue
112
+
113
+ message = (
114
+ f"<link type=\"url\" value=\"{item['link']}\">"
115
+ f"{item['title']}"
116
+ f"</link>\n\n"
117
+ f"{item['description']}"
118
+ )
119
+
120
+ send_to_channel(message)
121
+ sent_log.add(link_hash)
122
+ new_count += 1
123
+
124
+ time.sleep(1) # 連投防止
125
+
126
+ if new_count > 0:
127
+ save_sent_log(sent_log)
128
+ print(f"{new_count} 件のニュースを送信しました")
129
+ else:
130
+ print("新しいニュースはありません")
131
+
132
+ except Exception as e:
133
+ print("エラー:", e)
134
+
135
+ time.sleep(INTERVAL_SECONDS)
136
+
137
+
138
+ if __name__ == "__main__":
139
+ main()