crazymanish / fastlane-plugin-slack_bot

A Fastlane plugin to customize your automation workflow(s) with a Slack Bot 🤖🚀
MIT License
38 stars 5 forks source link

file_upload_to_slack() Fails Due to Deprecated files.upload API #12

Open ergl1s opened 1 month ago

ergl1s commented 1 month ago

The files.upload API used in this plugin has been deprecated for newly created Slack apps. This deprecation affects the file_upload_to_slack() function, which now returns the following response:

{
    "ok": false,
    "error": "method_deprecated"
}

Current API will continue to function only for old Slack apps until March 11, 2025.

Current documentation for the updated file uploading process can be found here: Slack API Documentation - Uploading Files.

mohamed-sultan commented 2 weeks ago

@ergl1s how did u overcome this issue?

ergl1s commented 2 weeks ago

@mohamed-sultan I created my own implementation of uploading file to slack according to the latest Documentation.

I wrote ruby functions. To use them with fastlane you should import this file via import("your-relative-path-to-file.rb").

Here's code on ruby:

def my_file_upload_to_slack(initial_comment:, file_path:, channel_id:, thread_ts:, api_token:)
  UI.message("🚀 Initiating my file upload to Slack...")

  file_name = File.basename(file_path)
  file_size = File.size(file_path)

  upload_url, file_id = fetch_slack_upload_url_and_file_id(
    filename: file_name,
    file_size: file_size,
    api_token: api_token
  )

  return unless upload_url && file_id

  return unless perform_file_upload_to_slack(
    upload_url: upload_url, 
    file_path: file_path, 
    api_token: api_token
  )

  complete_file_upload_to_slack(
    file_id: file_id, 
    file_name: file_name, 
    channel_id: channel_id, 
    thread_ts: thread_ts, 
    initial_comment: initial_comment, 
    api_token: api_token
  )
end

def fetch_slack_upload_url_and_file_id(filename:, file_size:, api_token:)
  uri = URI("https://slack.com/api/files.getUploadURLExternal?filename=#{filename}&length=#{file_size}")

  request = Net::HTTP::Get.new(uri)
  request['Authorization'] = "Bearer #{api_token}"

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end

  result = JSON.parse(response.body)

  if result['ok']
    UI.message("✅ Successfully obtained upload URL and file ID.")
    return result['upload_url'], result['file_id']
  else
    UI.error("❌ Slack API error: #{result}")
    return nil, nil
  end
end

def perform_file_upload_to_slack(upload_url:, file_path:, api_token:)
  uri = URI(upload_url)

  file_data = File.binread(file_path)
  file_size = file_data.bytesize

  request = Net::HTTP::Post.new(uri)
  request['Authorization'] = "Bearer #{api_token}"
  request['Content-Type'] = 'application/octet-stream'
  request.body = file_data

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end

  expected_response = "OK - #{file_size}"
  if response.body.strip == expected_response
    UI.message("✅ File data successfully uploaded to Slack.")
    return true
  else
    UI.error("❌ File upload failed. Unexpected response: #{response.body}")
    return false
  end
end

def complete_file_upload_to_slack(file_id:, file_name:, channel_id:, thread_ts:, initial_comment:, api_token:)
  uri = URI("https://slack.com/api/files.completeUploadExternal")

  body = {
    files: [
      {
        id: file_id,
        title: file_name
      }
    ],
    channel_id: channel_id,
    initial_comment: initial_comment
  }

  body[:thread_ts] = thread_ts if thread_ts

  request = Net::HTTP::Post.new(uri)
  request['Authorization'] = "Bearer #{api_token}"
  request['Content-Type'] = 'application/json'
  request.body = body.to_json

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end

  result = JSON.parse(response.body)

  if result['ok']
    UI.message("✅ Slack file upload process fully completed.")
    return true
  else
    UI.error("❌ Failed to complete file upload. Response: #{result}")
    return false
  end
end