require 'mysql2' require File.expand_path("../../../config/environment", __FILE__) require_relative 'base' class GossamerForumsImporter < ImportScripts::Base def initialize super # Initialize MySQL client with connection details @mysql_client = Mysql2::Client.new( host: "slowtwitch.northend.network", username: "admin", password: "yxnh93Ybbz2Nm8#mp28zCVv", database: "slowtwitch" ) end # Execute a query on the MySQL database def execute_query(query) @mysql_client.query(query, as: :hash) end # Sanitize username to comply with Discourse's rules def sanitize_username(username, email, name) original_username = username # Replace unacceptable characters with underscores sanitized = username.gsub(/[^a-zA-Z0-9._-]/, '_') # Ensure the username is at least 3 characters long sanitized = "#{sanitized}." if sanitized.length < 2 # Ensure the username is no more than 20 characters long sanitized = sanitized[0, 20] if sanitized.length > 20 original_sanitized = sanitized # Check for existing user with the same username existing_user = User.find_by(username: sanitized) if existing_user # If email and name match, do not modify the username if existing_user.email == email && existing_user.name == name return sanitized else # Ensure the username is unique counter = 1 while User.exists?(username: sanitized) sanitized = "#{original_sanitized}_#{counter}" sanitized = sanitized[0, 20] if sanitized.length > 20 counter += 1 end end end # Print the original and sanitized usernames if they differ if original_username != sanitized puts "Sanitized username: '#{original_username}' --> '#{sanitized}'" else puts "UNsanitized username: '#{original_username}' --> '#{sanitized}'" end sanitized end # Sanitize email to replace restricted domains def sanitize_email(email) # mailinator.com is not allowed by restricted_domains = ['mailinator.com', 'example.com'] # Add more restricted domains as needed domain = email.split('@').last if restricted_domains.include?(domain) sanitized_email = email.gsub(domain, 'email.invalid') # Change to a permissible domain puts "Sanitized email: '#{email}' --> '#{sanitized_email}'" return sanitized_email end email end # Import users from gforum_User table def import_users puts "Importing users..." users = [] execute_query("SELECT * FROM gforum_User").each do |row| users << { id: row['user_id'], username: sanitize_username(row['user_username'], row['user_email'], row['user_real_name']), email: row['user_email'], created_at: Time.at(row['user_registered']), updated_at: Time.at(row['user_last_seen']), name: row['user_real_name'], title: row['user_title'], bio_raw: row['user_about'], website: row['user_homepage'], location: row['user_location'] } end # Create users in Discourse with the required block create_users(users) do |user| user end # Update user passwords and import user files users.each do |user| discourse_username = sanitize_username(user[:username], user[:email], user[:name]) discourse_user = User.find_by(username: discourse_username) if discourse_user.nil? puts "User #{user[:username]} --> #{discourse_username} not found in Discourse. Skipping password update." next end if discourse_user.custom_fields.nil? discourse_user.custom_fields = {} end execute_query("SELECT * FROM gforum_User WHERE user_id = #{user[:id]}").each do |row| discourse_user.custom_fields['md5_password'] = row['user_password'] discourse_user.save_custom_fields end import_user_files(discourse_user) end end # Import user files and append to user's bio def import_user_files(user) print "\rImporting files for user #{user.username}..." execute_query("SELECT * FROM gforum_User_Files WHERE ForeignColName = 'user_id' AND ForeignColKey = #{user.id}").each do |file| # Construct file URL file_url = "https://forum.slowtwitch.com/images/users/images/#{file['ID'] % 10}/#{file['ID']}-#{file['File_Name']}" # Append image link to user's bio user.bio_raw += "\n\n![#{file['File_Name']}](#{file_url})" user.save! end print "\rImporting files for user #{user.username}... Done.\n" end # Import categories from gforum_Category table def import_categories puts "Importing categories..." execute_query("SELECT * FROM gforum_Category").each do |row| # Use current time if created_at or updated_at is null created_at = row['created_at'] ? Time.at(row['created_at']) : Time.now updated_at = row['updated_at'] ? Time.at(row['updated_at']) : Time.now create_category( id: row['category_id'], name: row['name'], description: row['description'], created_at: created_at, updated_at: updated_at ) end end # Import topics and posts from gforum_Post table def import_topics_and_posts puts "Importing topics and posts..." execute_query("SELECT * FROM gforum_Post ORDER BY post_root_id, post_time").each do |row| if row['post_id'] == row['post_root_id'] # This is the root post, create a new topic topic = create_topic( id: row['post_id'], title: row['post_subject'], user_id: row['user_id_fk'], created_at: Time.at(row['post_time']), updated_at: Time.at(row['post_latest_reply']), category_id: row['forum_id_fk'] ) # Create the first post in the topic create_post( id: row['post_id'], topic_id: row['post_id'], user_id: row['user_id_fk'], raw: import_post_attachments(row['post_message'], row['post_id']), created_at: Time.at(row['post_time']), updated_at: Time.at(row['post_latest_reply']) ) else # This is a reply post, add to the existing topic create_post( id: row['post_id'], topic_id: row['post_root_id'], user_id: row['user_id_fk'], raw: import_post_attachments(row['post_message'], row['post_id']), created_at: Time.at(row['post_time']), updated_at: Time.at(row['post_latest_reply']), reply_to_post_number: row['post_father_id'] ) end end end # Import post attachments from gforum_PostAttachment table def import_post_attachments(post_message, post_id) # Query for attachments related to the post attachments = execute_query("SELECT * FROM gforum_PostAttachment WHERE post_id_fk = #{post_id}") attachments.each do |attachment| # Append attachment link to the post message post_message += "\n\n![#{attachment['postatt_filename']}](https://forum.slowtwitch.com/forum/?do=post_attachment;postatt_id=#{attachment['postatt_filename']})" end post_message end # Import personal messages (both inbox and sent messages) def import_personal_messages puts "Importing personal messages..." import_inbox_messages import_sent_messages end # Import inbox messages from gforum_Message table def import_inbox_messages puts "Importing inbox messages..." execute_query("SELECT * FROM gforum_Message").each do |row| # Create a private message topic in Discourse topic = create_topic( title: row['msg_subject'], user_id: row['from_user_id_fk'], archetype: Archetype.private_message, created_at: Time.at(row['msg_time']), updated_at: Time.at(row['msg_time']) ) # Create the message as a post in the private topic create_post( topic_id: topic.id, user_id: row['from_user_id_fk'], raw: row['msg_body'], created_at: Time.at(row['msg_time']), updated_at: Time.at(row['msg_time']) ) # Add recipient user to the private message topic topic.add_user_by_id(row['to_user_id_fk']) topic.save! end end # Import sent messages from gforum_SentMessage table def import_sent_messages puts "Importing sent messages..." execute_query("SELECT * FROM gforum_SentMessage").each do |row| # Create a private message topic in Discourse topic = create_topic( title: row['msg_subject'], user_id: row['from_user_id_fk'], archetype: Archetype.private_message, created_at: Time.at(row['msg_time']), updated_at: Time.at(row['msg_time']) ) # Create the message as a post in the private topic create_post( topic_id: topic.id, user_id: row['from_user_id_fk'], raw: row['msg_body'], created_at: Time.at(row['msg_time']), updated_at: Time.at(row['msg_time']) ) # Add recipient user to the private message topic topic.add_user_by_id(row['to_user_id_fk']) topic.save! end end # Perform the full import process def perform_import import_users import_categories import_topics_and_posts import_personal_messages end end # Create an instance of the importer and start the import process importer = GossamerForumsImporter.new importer.perform_import