class FeishuApiClient
def commit_version(app_id, version_id)
def commit_version(app_id, version_id) post_json("#{FEISHU_API_BASE}/publish/commit/#{app_id}/#{version_id}", {}) end
def create_app(name, desc)
def create_app(name, desc) post_json("#{FEISHU_API_BASE}/app/create", { appSceneType: 0, name: name, desc: desc, avatar: "", i18n: { zh_cn: { name: name, description: desc } }, primaryLang: "zh_cn" }) end
def create_version(app_id, version: "1.0.0")
def create_version(app_id, version: "1.0.0") post_json("#{FEISHU_API_BASE}/app_version/create/#{app_id}", { clientId: app_id, appVersion: version, changeLog: "Initial release", autoPublish: false, pcDefaultAbility: "bot", mobileDefaultAbility: "bot" }) end
def enable_bot(app_id)
def enable_bot(app_id) post_json("#{FEISHU_API_BASE}/robot/switch/#{app_id}", { enable: true }) end
def get_all_scopes(app_id)
def get_all_scopes(app_id) get_json("#{FEISHU_API_BASE}/scope/all/#{app_id}") end
def get_app_info(app_id)
def get_app_info(app_id) get_json("#{FEISHU_API_BASE}/app/#{app_id}") end
def get_event(app_id)
def get_event(app_id) get_json("#{FEISHU_API_BASE}/event/#{app_id}") end
def get_json(url)
Execute a GET fetch in the browser page context.
def get_json(url) js = <<~JS (async () => { const csrfToken = window.csrfToken || ''; const resp = await fetch(#{url.to_json}, { method: 'GET', credentials: 'include', headers: { 'accept': '*/*', 'x-timezone-offset': '-480', 'x-csrf-token': csrfToken } }); return await resp.text(); })() JS run_fetch(js, url) end
def get_secret(app_id)
def get_secret(app_id) get_json("#{FEISHU_API_BASE}/secret/#{app_id}") end
def initialize(browser_session)
def initialize(browser_session) @browser = browser_session end
def post_json(url, payload)
Execute a POST fetch in the browser page context.
def post_json(url, payload) js = <<~JS (async () => { const csrfToken = window.csrfToken || ''; const resp = await fetch(#{url.to_json}, { method: 'POST', credentials: 'include', headers: { 'accept': '*/*', 'content-type': 'application/json', 'origin': 'https://open.feishu.cn', 'referer': 'https://open.feishu.cn/app', 'x-timezone-offset': '-480', 'x-csrf-token': csrfToken }, body: #{JSON.generate(payload).to_json} }); return await resp.text(); })() JS run_fetch(js, url) end
def release_version(app_id, version_id)
def release_version(app_id, version_id) post_json("#{FEISHU_API_BASE}/publish/release/#{app_id}/#{version_id}", { clientId: app_id, versionId: version_id }) end
def run_fetch(js, url)
def run_fetch(js, url) raw = @browser.evaluate(js) # The MCP tool may wrap result in markdown code blocks json_text = @browser.send(:extract_string_value, raw) JSON.parse(json_text) rescue JSON::ParserError => e raise "JSON parse error from #{url}: #{e.message} — raw: #{raw.to_s[0..300]}" end
def switch_callback_mode(app_id, mode: 4)
def switch_callback_mode(app_id, mode: 4) post_json("#{FEISHU_API_BASE}/callback/switch/#{app_id}", { callbackMode: mode }) end
def switch_event_mode(app_id, mode: 4)
def switch_event_mode(app_id, mode: 4) post_json("#{FEISHU_API_BASE}/event/switch/#{app_id}", { eventMode: mode }) end
def update_event(app_id, event_mode:)
def update_event(app_id, event_mode:) post_json("#{FEISHU_API_BASE}/event/update/#{app_id}", { operation: "add", events: ["im.message.receive_v1"], eventMode: event_mode }) end
def update_scopes(app_id, scope_ids)
def update_scopes(app_id, scope_ids) post_json("#{FEISHU_API_BASE}/scope/update/#{app_id}", { clientId: app_id, appScopeIDs: scope_ids, userScopeIDs: [], scopeIds: [], operation: "add" }) end