|
1 | 1 | require 'rss' |
| 2 | +require 'net/http' |
| 3 | +require 'json' |
2 | 4 |
|
3 | 5 | NEWS_YAML_PATH = 'db/news.yml'.freeze |
4 | 6 | NEWS_LOG_PATH = 'log/news.log'.freeze |
@@ -99,6 +101,121 @@ namespace :news do |
99 | 101 | logger.info "" |
100 | 102 | end |
101 | 103 |
|
| 104 | + desc "news.yml をリセットし、すべてのフィードから全記事を取得" |
| 105 | + task 'fetch:reset' => :environment do |
| 106 | + # ロガー設定(ファイル+コンソール出力) |
| 107 | + console = ActiveSupport::Logger.new(STDOUT) |
| 108 | + logger_file = ActiveSupport::Logger.new(NEWS_LOG_PATH) |
| 109 | + logger = ActiveSupport::BroadcastLogger.new(logger_file, console) |
| 110 | + |
| 111 | + logger.info('==== START news:fetch:reset ====') |
| 112 | + |
| 113 | + # 1. news.yml を空にする |
| 114 | + File.write(NEWS_YAML_PATH, [].to_yaml) |
| 115 | + logger.info("📄 news.yml をリセットしました") |
| 116 | + |
| 117 | + # 2. WordPress REST API からすべての投稿を取得 |
| 118 | + dojo_news_items = fetch_all_wordpress_posts(logger) |
| 119 | + logger.info("📰 news.coderdojo.jp から #{dojo_news_items.size} 件を取得") |
| 120 | + |
| 121 | + # 3. PR TIMES RSS フィードからすべてのプレスリリースを取得 |
| 122 | + prtimes_items = fetch_prtimes_feed(logger) |
| 123 | + logger.info("📢 PR TIMES から #{prtimes_items.size} 件を取得") |
| 124 | + |
| 125 | + # 4. すべてのアイテムをマージし、ID を付与 |
| 126 | + all_items = (dojo_news_items + prtimes_items).sort_by { |item| |
| 127 | + Time.parse(item['published_at']) |
| 128 | + } |
| 129 | + |
| 130 | + # ID を付与(古い順で1から) |
| 131 | + all_items.each.with_index(1) do |item, index| |
| 132 | + item['id'] = index |
| 133 | + end |
| 134 | + |
| 135 | + # 最新順にソート |
| 136 | + sorted_items = all_items.sort_by { |item| |
| 137 | + Time.parse(item['published_at']) |
| 138 | + }.reverse |
| 139 | + |
| 140 | + # 5. YAML ファイルに書き出し |
| 141 | + File.open(NEWS_YAML_PATH, 'w') do |f| |
| 142 | + formatted_items = sorted_items.map do |item| |
| 143 | + { |
| 144 | + 'id' => item['id'], |
| 145 | + 'url' => item['url'], |
| 146 | + 'title' => item['title'], |
| 147 | + 'published_at' => item['published_at'] |
| 148 | + } |
| 149 | + end |
| 150 | + |
| 151 | + f.write(formatted_items.to_yaml) |
| 152 | + end |
| 153 | + |
| 154 | + logger.info("✅ 合計 #{sorted_items.size} 件を news.yml に保存しました") |
| 155 | + logger.info("📌 次は 'bundle exec rails news:upsert' でデータベースに反映してください") |
| 156 | + logger.info("==== END news:fetch:reset ====") |
| 157 | + end |
| 158 | + |
| 159 | + # WordPress REST API からすべての投稿を取得 |
| 160 | + def fetch_all_wordpress_posts(logger) |
| 161 | + items = [] |
| 162 | + page = 1 |
| 163 | + per_page = 100 |
| 164 | + |
| 165 | + loop do |
| 166 | + uri = URI("https://news.coderdojo.jp/wp-json/wp/v2/posts") |
| 167 | + uri.query = URI.encode_www_form(page: page, per_page: per_page, status: 'publish') |
| 168 | + |
| 169 | + response = Net::HTTP.get_response(uri) |
| 170 | + break unless response.is_a?(Net::HTTPSuccess) |
| 171 | + |
| 172 | + posts = JSON.parse(response.body) |
| 173 | + break if posts.empty? |
| 174 | + |
| 175 | + posts.each do |post| |
| 176 | + items << { |
| 177 | + 'url' => post['link'], |
| 178 | + 'title' => post['title']['rendered'], |
| 179 | + 'published_at' => Time.parse(post['date_gmt'] + ' UTC').iso8601 |
| 180 | + } |
| 181 | + end |
| 182 | + |
| 183 | + logger.info("📄 WordPress API: ページ #{page} から #{posts.size} 件取得") |
| 184 | + page += 1 |
| 185 | + end |
| 186 | + |
| 187 | + items |
| 188 | + end |
| 189 | + |
| 190 | + # PR TIMES RSS フィードから全記事を取得 |
| 191 | + def fetch_prtimes_feed(logger) |
| 192 | + items = [] |
| 193 | + |
| 194 | + begin |
| 195 | + feed = RSS::Parser.parse('https://prtimes.jp/companyrdf.php?company_id=38935', false) |
| 196 | + |
| 197 | + feed.items.each do |item| |
| 198 | + published_at = if item.respond_to?(:dc_date) && item.dc_date |
| 199 | + item.dc_date.iso8601 |
| 200 | + else |
| 201 | + raise "PR TIMES feed: dc:date not found for item: #{item.link}" |
| 202 | + end |
| 203 | + |
| 204 | + items << { |
| 205 | + 'url' => item.link, |
| 206 | + 'title' => item.title, |
| 207 | + 'published_at' => published_at |
| 208 | + } |
| 209 | + end |
| 210 | + |
| 211 | + logger.info("📢 PR TIMES RSS: #{items.size} 件取得") |
| 212 | + rescue => e |
| 213 | + logger.error("❌ PR TIMES フィード取得エラー: #{e.message}") |
| 214 | + end |
| 215 | + |
| 216 | + items |
| 217 | + end |
| 218 | + |
102 | 219 | desc "#{NEWS_YAML_PATH} からデータベースに upsert" |
103 | 220 | task upsert: :environment do |
104 | 221 | console = ActiveSupport::Logger.new(STDOUT) |
|
0 commit comments