Skip to content

Instantly share code, notes, and snippets.

@icelander
Last active May 27, 2022 22:04
Show Gist options
  • Save icelander/80874e9d6f362146bd421265b79bfafa to your computer and use it in GitHub Desktop.
Save icelander/80874e9d6f362146bd421265b79bfafa to your computer and use it in GitHub Desktop.
Migrates custom emoji from one Mattermost server to another
#!/usr/bin/env ruby
# frozen_string_literal: true
## migrate_custom_emoji.rb
#
## ABOUT
#
# This script migrates custom emoji from one Mattermost instance to another.
# While it can be run on the source, any machine that can access both
# servers via HTTP that has a copy of the `/opt/mattermost/data/emoji`
# directory will work.
#
## HOW TO USE
#
# 1. Install Ruby and the HTTParty gem
# 2. Put this script somewhere on your Mattermost server
# 3. Generate a personal access token with admin privileges in both servers
# 4. Change these values to match your environment. Be sure to quote the
# strings properly, e.g.
#
# source_server = 'this is correct'
# auth_token = this is not correct
# emoji_path = 'Neither is this"
# destination_server = ''Also incorrect''
#
# URL of the source Mattermost server
source_server = 'https://mattermost.movetoiceland.com/'
# Authentication token for source Mattermost server
source_token = 'qtmwjk6q8igyfqm491u9unbk1w'
# Path to Emoji Directory, NOTE: Use '/opt/mattermost/data/emoji' if you are
# running this on the source Mattermost server
emoji_path = './emoji/'
# URL of the destination Mattermost server
destination_server = 'https://chat.iclnd.me/'
# Authentication token for source Mattermost server
destination_token = '871nkkt8ht8ad8urfonefxadce'
#
# 5. Run the script through Ruby and use tee to capture the output:
#
# ruby migrate_custom_emoji.rb | tee migrate_custom_emoji.log
#
# 6. Check the destination for the custom emoji
# 7. (Optional) Delete the personal access tokens and this script for security
#######################################
### DO NOT EDIT ANYTHING BELOW HERE ###
#######################################
require 'httparty'
require 'uri'
require 'fileutils'
def check_version(min_version, level=0)
if min_version.kind_of?(String)
min_version = min_version.split('.')
end
if level > min_version.length
return true
end
ruby_version = RUBY_VERSION.split('.')
if min_version[level].to_i < ruby_version[level].to_i
return true
elsif min_version[level].to_i == ruby_version[level].to_i
check_version(min_version, level+1)
else
return false
end
end
min_version = '2.5.1'
if ! check_version(min_version)
puts "Please install Ruby version #{min_version}"
end
class MattermostApi
include HTTParty
format :json
# UNCOMMENT NEXT LINE TO DEBUG REQUESTS
# debug_output $stdout
def initialize(mattermost_url, auth_token)
# Default Options
@options = {
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'HTTParty On, Dudes!'
},
# TODO Make this more secure
verify: false
}
# check the config for mattermost_url
if mattermost_url.nil? or !url_valid?(mattermost_url)
raise 'url is required in configuration'
else
if mattermost_url[-1] != '/'
mattermost_url = mattermost_url + '/'
end
end
@base_uri = mattermost_url + 'api/v4/'
@options[:headers]['Authorization'] = "Bearer #{auth_token}"
@options[:body] = nil
@user = nil
test_response = self.class.get("#{@base_uri}users/me", @options)
if test_response.code == 200
@user = JSON.parse(test_response.to_s)
else
puts "Could not connect to Mattermost server #{mattermost_url}, aborting."
exit 1
end
if @user.nil? || @user['id'].nil?
puts "Could not get current user, aborting."
exit 1
end
end
def get_custom_emoji
return self.get_all('emoji')
end
def emoji_exists?(emoji_name)
url = "emoji/name/#{emoji_name}"
return self.get_exists(url, emoji_name, 'name')
end
def upload_emoji(emoji_name, image_path)
post_body = {
emoji: {
name: emoji_name,
creator_id: @user['id']
}.to_json,
image: File.open(image_path)
}
upload_response = self.class.post(
@base_uri + 'emoji',
multipart: true,
body: post_body,
headers: @options[:headers]
)
if upload_response.code == 200
return JSON.parse(upload_response.to_s)
else
puts "ERROR Could not upload #{emoji_name}"
return false
end
end
def post_data(payload, request_url)
options = @options
options[:body] = payload.to_json
return self.class.post("#{@base_uri}#{request_url}", options)
end
def url_valid?(url)
url = URI.parse(url) rescue false
end
def get_all(url)
results = Array.new
per_page = 60
page = 0
loop do
page_url = url + "?per_page=#{per_page}&page=#{page}"
returned = self.get_url(page_url)
results += returned
page += 1
break if returned.count != per_page
end
return results
end
def get_url(url)
JSON.parse(self.class.get("#{@base_uri}#{url}", @options).to_s)
end
def get_exists(url, compare_value=nil, compare_field=nil)
# How does one test that something exists?
# Well, get the url and if it returns 200
uri = @base_uri + url
resp = self.class.get(uri, @options)
case resp.code
when 200
if !compare_field.nil? and !compare_value.nil?
result = JSON.parse(resp.to_s)
if !result[compare_field].nil?
if compare_value == result[compare_field]
return true
else
return false
end
else
raise "Error checking for emoji existence"
end
else
return true
end
when 404
return false
else
raise "Error checking existence"
end
if resp.code == 200
else
return false
end
end
end
puts '=' * 80
output = <<EOF
Migrating custom emoji with these settings
- Source: #{source_server}
- Emoji Directory Path: #{emoji_path}
- Destination: #{destination_server}
Beginning Migration....
EOF
puts output
puts '=' * 80
$source_mm = MattermostApi.new(source_server, source_token)
$destination_mm = MattermostApi.new(destination_server, destination_token)
# Get the custom emoji as an array
custom_emoji_list = $source_mm.get_custom_emoji()
count_total = 0
count_migrated = 0
count_errors = 0
puts "Found #{custom_emoji_list.length} custom emoji on source server"
custom_emoji_list.each do |emoji|
count_total += 1
begin
if $destination_mm.emoji_exists?(emoji['name'])
puts ":#{emoji['name']}: exists on destination, skipping"
count_errors = count_errors + 1
next
end
rescue Exception => e
count_errors = count_errors + 1
puts "Error checking for :#{emoji['name']}: existence on destination, skipping"
next
end
emoji_dir = emoji_path + emoji['id']
image_file = emoji_path + emoji['id'] + '/image'
if File.readable?(image_file)
resp = $destination_mm.upload_emoji(emoji['name'], image_file)
if resp
count_migrated = count_migrated + 1
puts ":#{resp['name']}: migrated!"
else
count_errors = count_errors + 1
end
else
puts ":#{emoji['name']}: image file #{image_file} can't be read, skipping"
count_errors = count_errors + 1
end
end
puts '=' * 80
output = <<EOF
Migration Complete!
Results:
- Total Emoji Found: #{custom_emoji_list.length}
- Total Emoji Processed: #{count_total}
- Total Migrated: #{count_migrated}
- Total Errors: #{count_errors}
EOF
puts output
puts '=' * 80
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment