A => .gitignore +1 -0
A => README.md +26 -0
@@ 1,26 @@
+# Download custom Slack emoji without admin access
+
+You'll need the `Cookie` header, and the slack token to download the list of emoji. To get those, run the Slack team in a web browser. Open up the network panel, open the emoji panel and do a search. You'll see a `POST` request to something like: `https://edgeapi.slack.com/cache/<TEAM_ID>/emojis/search`. Inspect that request, note these values as environment variables in your shell:
+
+* `SLACK_TEAM_ID`: The `TEAM_ID` in the `search` url
+* `SLACK_TOKEN`: The `POST` body has a `token` with some dashes
+* `SLACK_COOKIE`: The `POST` headers will have a `Cookie` header. You need the long header _value_ only, not the whole header.
+* `SLACK_PLATFORM`: Optional. `apple` or `google`. Defaults to `apple`
+ * This is because some emoji have platform-specific variants. Setting this variable sets which platform image to download.
+
+You can set these like so:
+
+```sh
+export SLACK_TEAM_ID="TXXXXX"
+export SLACK_TOKEN="xoxc-xxxxxx-xxxxxx"
+export SLACK_COOKIE="x=l8f8gls55xf; foo=...; bar=..."
+export SLACK_PLATFORM="apple"
+```
+
+Then run the script:
+
+```ruby
+ruby fetch_emoji.rb
+```
+
+First it downloads the list of all the emoji files. Then each emoji image. Emojis will download to a directory named `downloads`. All emoji aliases are skipped.
A => fetch_emoji.rb +95 -0
@@ 1,95 @@
+require "fileutils"
+require "json"
+require "net/http"
+require "pathname"
+
+class Config
+ def self.download_folder
+ "downloaded"
+ end
+end
+
+class EmojiDownloader
+ PAGE_COUNT = 100
+
+ def initialize
+ team_id = ENV.fetch("SLACK_TEAM_ID")
+ @emoji_list_uri = URI("https://edgeapi.slack.com/cache/#{team_id}/emojis/list")
+ FileUtils.mkdir_p(Config.download_folder)
+ end
+
+ def download
+ download_list.each do |emoji_data|
+ emoji = EmojiData.new(emoji_data)
+ next if emoji.alias?
+
+ image_response = Net::HTTP.get_response(URI(emoji.download_uri))
+ File.open(emoji.local_path, "wb") do |file|
+ file.write(image_response.body)
+ end
+ puts "saved: #{emoji.local_path}"
+ end
+ end
+
+ private
+
+ def download_list(marker: nil)
+ puts "fetching metadata: (start)" if marker.nil?
+ puts "fetching metadata at: #{marker}" if marker
+ response = Net::HTTP.post(@emoji_list_uri, body(marker: marker), headers).body
+ json = JSON.parse(response, symbolize_names: true)
+ next_marker = json[:next_marker]
+
+ if next_marker
+ json[:results] + download_list(marker: next_marker)
+ else
+ json[:results]
+ end
+ end
+
+ def body(marker: nil)
+ { token: ENV.fetch("SLACK_TOKEN"), count: PAGE_COUNT }.tap do |body|
+ body[:marker] = marker if marker
+ end.to_json
+ end
+
+ def headers
+ { "Cookie" => ENV.fetch("SLACK_COOKIE") }
+ end
+end
+
+class EmojiData
+ def initialize(data)
+ @data = data
+ end
+
+ def local_path
+ path = Pathname.new(download_uri)
+ "#{Config.download_folder}/#{@data[:name]}#{path.extname}"
+ end
+
+ def download_uri
+ if platform_variants?
+ platform_download_uri
+ else
+ @data[:value]
+ end
+ end
+
+ def platform_download_uri
+ platform = ENV.fetch("SLACK_PLATFORM", :apple).to_sym
+ @data[:value].fetch(platform)
+ end
+
+ def alias?
+ @data.fetch(:is_alias, false)
+ end
+
+ private
+
+ def platform_variants?
+ @data[:value].is_a?(Hash) && (@data[:value][:apple] || @data[:value][:google])
+ end
+end
+
+EmojiDownloader.new.download