Skip to content

Commit 6f7aa68

Browse files
authored
Merge pull request #1709 from coderdojo-japan/enable-c-search-to-cver-connpass-group-root-url
bin/c-search: グループURLからgroup_id取得機能を追加
2 parents 75f6716 + 0683f93 commit 6f7aa68

File tree

1 file changed

+129
-22
lines changed

1 file changed

+129
-22
lines changed

bin/c-search

Lines changed: 129 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,148 @@
22

33
require 'connpass_api_v2'
44
require 'uri'
5+
require 'net/http'
6+
require 'json'
7+
require 'timeout'
58

69
if ENV['CONNPASS_API_KEY'].nil?
710
puts('CONNPASS_API_KEY が設定されていません')
811
exit(1)
912
end
1013

1114
if ARGV.empty?
12-
puts('Usage: c-search [CONNPASS_EVENT_URL | CONNPASS_EVENT_ID]')
15+
puts('Usage: c-search [CONNPASS_URL | CONNPASS_EVENT_ID]')
16+
puts(' 例: c-search https://coderdojoaoyama.connpass.com/')
17+
puts(' 例: c-search https://coderdojoaoyama.connpass.com/event/356972/')
18+
puts(' 例: c-search 356972')
1319
exit(1)
1420
end
1521

1622
input = ARGV[0]
17-
event_id = nil
18-
if input =~ /^https?:\/\//
19-
# URLからイベントIDを抽出
20-
event_id = URI(input).path[%r{event/(\d+)}, 1]
21-
else
22-
event_id = input.gsub(/\D/, '')
23+
24+
# URLのバリデーションとタイプ判定
25+
def validate_and_classify_url(input)
26+
# 数字のみの場合はイベントID
27+
return { type: :event_id, value: input.gsub(/\D/, '') } if input !~ /^https?:\/\//
28+
29+
begin
30+
uri = URI.parse(input)
31+
32+
# HTTPSのみ許可(セキュリティ対策)
33+
unless uri.scheme == 'https'
34+
return { type: :error, message: "HTTPSのURLを指定してください: #{input}" }
35+
end
36+
37+
# Connpassドメインのみ許可(SSRF対策)
38+
unless uri.host&.end_with?('.connpass.com')
39+
return { type: :error, message: "Connpass のURLを指定してください: #{input}" }
40+
end
41+
42+
# イベントURLの場合
43+
if uri.path =~ %r{/event/(\d+)/?}
44+
return { type: :event_url, event_id: $1 }
45+
end
46+
47+
# グループURLの場合
48+
if uri.path == '/' || uri.path.empty?
49+
subdomain = uri.host.split('.').first
50+
return { type: :group_url, subdomain: subdomain }
51+
end
52+
53+
return { type: :error, message: "認識できないURLパターンです: #{input}" }
54+
rescue URI::InvalidURIError => e
55+
return { type: :error, message: "無効なURLです: #{input}" }
56+
end
2357
end
2458

25-
unless event_id && !event_id.empty?
26-
puts "イベントIDが特定できませんでした: #{input}"
27-
exit 1
59+
# グループ情報を取得する関数(リダイレクト対応)
60+
def fetch_group_by_subdomain(subdomain, api_key, limit = 5)
61+
return { success: false, message: "リダイレクトが多すぎます" } if limit <= 0
62+
63+
uri = URI('https://connpass.com/api/v2/groups/')
64+
uri.query = URI.encode_www_form(subdomain: subdomain, count: 1)
65+
66+
req = Net::HTTP::Get.new(uri)
67+
req['X-Api-Key'] = api_key
68+
69+
begin
70+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true,
71+
open_timeout: 5, read_timeout: 5) do |http|
72+
http.request(req)
73+
end
74+
75+
case res
76+
when Net::HTTPSuccess
77+
data = JSON.parse(res.body)
78+
return { success: false, message: "Invalid API response" } unless data.is_a?(Hash)
79+
80+
if data['results_returned'] && data['results_returned'] > 0
81+
groups = data['groups']
82+
return { success: false, message: "Invalid API response: missing groups" } unless groups&.any?
83+
84+
group = groups.first
85+
group_id = group&.dig('id')
86+
return { success: false, message: "Invalid API response: missing group ID" } unless group_id
87+
88+
return { success: true, group_id: group_id }
89+
else
90+
return { success: false, message: "グループが見つかりませんでした (subdomain: #{subdomain})" }
91+
end
92+
when Net::HTTPRedirection # 301, 302などのリダイレクト
93+
location = res['location']
94+
if location
95+
# 新しいURIでリトライ
96+
new_uri = URI.join(uri, location)
97+
# リダイレクト先のURIから新しいsubdomainを抽出
98+
new_subdomain = new_uri.host&.split('.')&.first
99+
return { success: false, message: "Invalid redirect URL" } unless new_subdomain
100+
return fetch_group_by_subdomain(new_subdomain, api_key, limit - 1)
101+
else
102+
return { success: false, message: "リダイレクト先が不明です" }
103+
end
104+
when Net::HTTPNotFound
105+
return { success: false, message: "グループが見つかりませんでした (subdomain: #{subdomain})" }
106+
else
107+
return { success: false, message: "APIエラー: #{res.code} #{res.message}" }
108+
end
109+
rescue Timeout::Error
110+
return { success: false, message: "APIへの接続がタイムアウトしました" }
111+
rescue => e
112+
return { success: false, message: "エラーが発生しました: #{e.message}" }
113+
end
28114
end
29115

30-
client = ConnpassApiV2.client(ENV['CONNPASS_API_KEY'])
31-
result = client.get_events(event_id: event_id)
32-
33-
if result.results_returned > 0
34-
event = result.events.first
35-
puts event.fetch('group').fetch('id')
36-
#puts "id: #{event.fetch('id')}"
37-
#puts "title: #{event.fetch('title')}"
38-
#puts "group_id: #{event.fetch('group').fetch('id')}"
39-
#puts "group_name: #{event.fetch('group').fetch('title')}"
40-
else
41-
puts "イベントが見つかりませんでした (event_id: #{event_id})"
116+
# メイン処理
117+
result = validate_and_classify_url(input)
118+
119+
case result[:type]
120+
when :error
121+
puts result[:message]
122+
exit 1
123+
124+
when :event_id, :event_url
125+
# イベントIDまたはイベントURLの場合(既存の処理)
126+
event_id = result[:type] == :event_id ? result[:value] : result[:event_id]
127+
128+
client = ConnpassApiV2.client(ENV['CONNPASS_API_KEY'])
129+
api_result = client.get_events(event_id: event_id)
130+
131+
if api_result.results_returned > 0
132+
event = api_result.events.first
133+
puts event.fetch('group').fetch('id')
134+
else
135+
puts "イベントが見つかりませんでした (event_id: #{event_id})"
136+
exit 1
137+
end
138+
139+
when :group_url
140+
# グループURLの場合(新規処理)
141+
group_result = fetch_group_by_subdomain(result[:subdomain], ENV['CONNPASS_API_KEY'])
142+
143+
if group_result[:success]
144+
puts group_result[:group_id]
145+
else
146+
puts group_result[:message]
147+
exit 1
148+
end
42149
end

0 commit comments

Comments
 (0)