Class: FeishuApiClient

Inherits:
Object
  • Object
show all
Defined in:
lib/clacky/default_skills/channel-setup/feishu_setup.rb

Overview


FeishuApiClient — calls Feishu internal API via the browser (fetch in page context). This way the browser automatically includes all cookies, CSRF tokens, and session headers — we never need to extract them manually.


Instance Method Summary collapse

Constructor Details

#initialize(browser_session) ⇒ FeishuApiClient

Returns a new instance of FeishuApiClient.



211
212
213
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 211

def initialize(browser_session)
  @browser = browser_session
end

Instance Method Details

#commit_version(app_id, version_id) ⇒ Object



279
280
281
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 279

def commit_version(app_id, version_id)
  post_json("#{FEISHU_API_BASE}/publish/commit/#{app_id}/#{version_id}", {})
end

#create_app(name, desc) ⇒ Object



215
216
217
218
219
220
221
222
223
224
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 215

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

#create_version(app_id, version: "1.0.0") ⇒ Object



268
269
270
271
272
273
274
275
276
277
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 268

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

#enable_bot(app_id) ⇒ Object



230
231
232
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 230

def enable_bot(app_id)
  post_json("#{FEISHU_API_BASE}/robot/switch/#{app_id}", { enable: true })
end

#get_all_scopes(app_id) ⇒ Object



254
255
256
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 254

def get_all_scopes(app_id)
  get_json("#{FEISHU_API_BASE}/scope/all/#{app_id}")
end

#get_app_info(app_id) ⇒ Object



290
291
292
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 290

def get_app_info(app_id)
  get_json("#{FEISHU_API_BASE}/app/#{app_id}")
end

#get_event(app_id) ⇒ Object



238
239
240
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 238

def get_event(app_id)
  get_json("#{FEISHU_API_BASE}/event/#{app_id}")
end

#get_json(url) ⇒ Object

Execute a GET fetch in the browser page context. Uses window.csrfToken — required by all /developers/v1/ endpoints.



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 297

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

#get_secret(app_id) ⇒ Object



226
227
228
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 226

def get_secret(app_id)
  get_json("#{FEISHU_API_BASE}/secret/#{app_id}")
end

#post_json(url, payload) ⇒ Object

Execute a POST fetch in the browser page context. Uses window.csrfToken (set by Feishu’s own JS) — the correct token for /developers/v1/ APIs.



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 318

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

#release_version(app_id, version_id) ⇒ Object



283
284
285
286
287
288
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 283

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

#run_fetch(js, url) ⇒ Object



341
342
343
344
345
346
347
348
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 341

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

#switch_callback_mode(app_id, mode: 4) ⇒ Object



250
251
252
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 250

def switch_callback_mode(app_id, mode: 4)
  post_json("#{FEISHU_API_BASE}/callback/switch/#{app_id}", { callbackMode: mode })
end

#switch_event_mode(app_id, mode: 4) ⇒ Object



234
235
236
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 234

def switch_event_mode(app_id, mode: 4)
  post_json("#{FEISHU_API_BASE}/event/switch/#{app_id}", { eventMode: mode })
end

#update_event(app_id, event_mode:) ⇒ Object



242
243
244
245
246
247
248
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 242

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

#update_scopes(app_id, scope_ids) ⇒ Object



258
259
260
261
262
263
264
265
266
# File 'lib/clacky/default_skills/channel-setup/feishu_setup.rb', line 258

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