Migrate a vBulletin 4 forum to Discourse

안녕하세요. 제 해결책을 공유하고 싶습니다.

인용문(quote) 관련 문제에 대해 말씀드리면, 앞서 말씀드린 것처럼 다음과 같은 상황에서 정규식(regexp)이 인용문을 올바르게 캡처하지 못하는 문제가 있었습니다:

  • 사용자 이름과 게시글 ID가 이중 따옴표로 둘러싸여 있는 경우
  • 중첩된 인용문(nested quotes)의 경우

저는 다른 로직을 사용하여 검색 및 치환을 수행하기로 결정했습니다. 태그와 그 내용을 검색하는 대신, 태그만 검색하는 정규식을 사용했습니다:

기존 코드:
raw.gsub!(%r{\[quote="?([^;]+);(\d+)"?\](.+?)\[\/quote\]}im) do

변경된 코드:
raw.gsub!(%r{(\[QUOTE(="?([^;]+);(\d+)"?)?\])|(\[\/QUOTE\])}im) do

그리고 인용문의 출처를 결정하는 부분을 조금 수정했습니다:

      if $3 && $4
        if topic_lookup = topic_lookup_from_imported_post_id(post_id)
          post_number = topic_lookup[:post_number]
          topic_id = topic_lookup[:topic_id]
          "\n[quote=\"#{new_username},post:#{post_number},topic:#{topic_id}\"]\n"
        else
          "\n[quote=\"#{new_username}\"]\n"
        end
      elsif $5
        "\n[/quote]\n"        
      end

또한, 스포일러(spoiler) 코드도 변경했습니다. 기존에는 다음과 같이 흐릿하게 처리된 텍스트를 사용했는데,

    # [spoiler=Some hidden stuff]SPOILER HERE!![/spoiler]
    raw.gsub!(%r{\[spoiler="?(.+?)"?\](.+?)\[/spoiler\]}im) do
      "\n#{$1}\n[spoiler]#{$2}[/spoiler]\n"
    end

이것을 details 태그로 변환하도록 변경했습니다:

    raw.gsub!(%r{(\[spoiler(="?(.*?)"?)?\])|(\[\/spoiler\])}im) do
      if $3
        "\n[details=#{$3}]\n"
      elsif $1
        "\n[details]\n"
      elsif $4
        "\n[/details]\n"
      end
    end

vBulletin 환경에서는 스포일러가 흐릿하게 처리된 내용보다는 접혀 있는(collapsed) 내용을 의미하기 때문입니다. 따라서 vBulletin 가져오기 스크립트에서 스포일러를 흐릿한 스포일러 대신 details로 변환하는 것이 훨씬 적절하다고 생각합니다.

또한 mention 태그도 발견했습니다. 제 경우 vBulletin에서는 mention이 다음과 같이 보였습니다:
[mention=XXX]username[/mention]

스크립트에서 사용된 정규식은 태그 안에 사용자 ID가 포함될 수 있다는 점을 고려하지 않았습니다.

    # [MENTION]<username>[/MENTION]
    raw.gsub!(%r{\[mention\](.+?)\[/mention\]}i) do
      new_username = get_username_for_old_username($1)
      "@#{new_username}"
    end

이 부분도 제 방식대로 수정했습니다:

    # [MENTION]<username>[/MENTION]
    raw.gsub!(%r{\[mention(=\d+)?\](.+?)\[/mention\]}i) do
      new_username = get_username_for_old_username($2)
      "@#{new_username}"
    end
1개의 좋아요

24년 된 vBulletin 3을 실행 중이던 vBulletin 포럼을 이식하기 시작했을 때 이는 사실이 아니었습니다. 해당 스크립트에는 여러 호환성 문제와 기타 문제가 있었습니다. 그러나 vB4용 스크립트를 기반으로 vBulletin 3용 가져오기(importer)를 만드는 데 많은 노력을 기울였습니다.

개선된 스크립트는 Discourse에 포함되어 있으며, 이름은 vbulletin3.rb입니다. vB3 가져오기 스크립트의 사용법은 이 안내문에서 설명된 것과 동일합니다. 대신 bundle exec ruby script/import_scripts/vbulletin3.rb를 실행하면 됩니다.

vBulletin3 스크립트에는 다음과 같은 상당한 변경 사항/개선 사항이 있습니다:

  1. 포럼 권한이 복사됩니다.
  2. 포럼 모더레이터 그룹이 생성됩니다.
  3. 가입 가능한 사용자 그룹이 적절한 구성으로 생성됩니다.
  4. 포럼 중첩이 Discourse의 최대치인 3단계까지 가져오기됩니다.
  5. 모든 스레드와 게시물의 영구 링크(Permalinks)가 등록되어 링크 부패(link rot)를 방지합니다.
  6. 일부 기본 포럼 설정이 복사됩니다(예: 제목, 알림 이메일, 회사 이름).
  7. 투표(Polls)가 가져오기됩니다.
  8. bbcode → markdown 변환에 대한 주요 개선 사항이 적용됩니다.
  9. 스레드, 게시물, 첨부 파일로의 URL 심링크가 Discourse 참조로 변환됩니다. 이를 위해서는 환경 변수 FORUM_URL을 forum.hostname/path(프로토콜 제외)로 설정해야 합니다.

vBulletin의 개인 메시지(Private Messages)를 Discourse의 개인 메시지로 변환하는 대신, 사용자는 이전에 보낸 개인 메시지의 아카이브를 포함한 시스템 개인 메시지를 받게 됩니다. vBulletin의 개인 메시지 구조는 Discourse와 실제로 호환되지 않습니다. 이를 변환하려고 하면 vBulletin에서 개인 메시지가 어떻게 사용되었는지에 따라 일부 프라이버시가 노출될 수도 있습니다.

다른 가져오기 스크립트와 마찬가지로, 변환에 상당한 시간이 소요될 수 있습니다. 제 워크스테이션에서는 7,000명의 사용자, 16,000개의 스레드, 415,000개의 게시물에 대해 변환 스크립트가 5.5시간이 소요되었습니다. 후처리(post processing)에 얼마나 시간이 걸렸는지는 알 수 없으며, 이를 밤새도록 실행하도록 두었습니다. 시작부터 끝까지 포럼은 30시간 동안 중단되었습니다. 결국 결과에 대해 만족합니다.

2개의 좋아요

정말 복고풍이네요 :slight_smile:

포럼이 정말 멋집니다. 주제 행의 번갈아 나타나는 색상이 좋습니다.

이 시점에서 이 스레드와 가져오기 도구(importer) 모두 상당히 오래된 것 같습니다. 이 스레드의 도움으로 몇 가지 문제를 수정했지만, 현재 사용자 가져오기 단계에서 다음 오류로 막혀 있습니다. 해결 방법을 아시는 분 계신가요?

<internal:timev>:286:in at’: can’t convert NilClass into an exact number (TypeError)`

쿼리가 잘못되었거나, 테이블에서 어떤 값이 누락된 것입니다.

이 문제를 수년 후에야 언급하는 것이 다소 이상할 수 있지만, 현재 대량 가져오기 도구(bulk importer)를 사용하여 VB 가져오기를 진행 중인데 일부 이미지가 누락되어 있었습니다. 원인은 첨부 파일의 파일명이 다른 필드로 이동했기 때문이었습니다.

 SELECT a.filedataid attachment_id, a.userid user_id, a.filename filename
             FROM attachment a
            WHERE a.attachmentid = 383075;

NUMBER.attach 파일은 이제 attachment_id 필드가 아니라 filedataid 필드입니다. 따라서 스크립트에서 해당 쿼리를 업데이트해야 합니다.

vBulletin 4.25 포럼을 Discourse로 마이그레이션해 달라는 요청을 받았습니다… 이 스레드를 읽으면서 복잡한 감정이 드네요… 가능해 보이지만, 엄청난 수고와 시간 소모가 될 것 같습니다(지금 그 둘 다 피하고 싶은데)…

vBulletin 4.25용 업데이트된 스크립트가 어디에 있나요? 공식 페이지에는 3과 5 버전만 보입니다.

음, bulk imports 디렉토리에 몇 달 전만 만들어진 vbulletin과 vbulletin5 스크립트가 있습니다. 이 벌크 임포트를 실행하는 것은 꽤 까다롭고, 문서화도 잘 되어 있지 않습니다.

저는 100건 이상의 임포트를 수행해 봤는데, 스크립트를 어떤 이유로든 조정하지 않고 완료한 경우는 한 번도 없었던 것 같습니다.

정말 Ruby를 제대로 배우기 전에 여러 개의 임포트 스크립트를 작성했습니다(하지만 1980년대 중반에 한 교수님이 프로그래밍 언어 수업 후 주말과 책만 있으면 어떤 언어든 알 수 있다고 확신을 주셨는데, 대체로 맞았습니다.)

하지만 네, 상상하시는 것만큼이나, 혹은 그 이상으로 고통스럽고 시간을 많이 잡아먹을 가능성이 높습니다.

vbulletin 스크립트가 꽤 잘 작동할 것 같습니다. 100만 개 이상의 게시물과 사용자가 없는 한 벌크 임포트 스크립트를 권장하지는 않을 것 같습니다.

1개의 좋아요

도와줘서 고마워요 :slight_smile:

주말에 뭘 해야 할지 결정을 내려야 할 것 같네요 :smiley:

1개의 좋아요

이 부분에 대한 도움을 정말 많이 받고 싶습니다 :face_with_spiral_eyes:

스레드를 읽어보고 여기에서 본 몇 가지 단계를 따라갔지만 막혀버렸습니다.

  1. VPS에 ssh로 접속
  2. Docker 이미지로 진입
  3. mariadb-server 설치
  4. mysql 명령을 실행하여 데이터베이스를 만들려는데 ‘can’t connect to local server through socket’ 오류가 나요

여기 있는 설명은 몇 년 전 것인데, 몇몇 사람이 같은 오류를 겪은 걸 봤지만 해결책은 보이지 않네요.

최근에 이 과정을 해보신 분이 계신가요? 올바른 방향으로 이끌어줄 수 있을까요? 어디선가 놓치고 있는 단계별 가이드가 있는 것 같지는 않은데, 혹시 있을까요?

수정: 추가로 말씀드리면, 온갖 시도를 해본 끝에 로컬 호스트(도커가 아닌)에 mariadb의 도커 이미지를 설치하고 포트 노출을 시도했습니다. 이제 도커 이미지에서 DB에 연결할 수는 있는데… 스크립트를 실행하면 Gemfile: Undefined local variable or method ‘mysql2’ 오류가 나요. gemfile 설치를 시도해봤지만 실패하네요… 더 이상 트러블슈팅하기 전에, 오래된 정보와 잠재적으로 오래된 패키지를 사용하고 있다는 느낌이 들어서요…정말 혼란스럽고 지침이 필요해요!

도움 주시면 감사하겠습니다!

…나는 계속 시도했고 마침내 스크립트가 실행되는 단계까지 도달했습니다! 그러나 실행을 시작하면 다음과 같은 오류가 발생합니다:

"root@vps-xxxxxxxx-app:/var/www/discourse/script/import_scripts# bundle exec ruby vbulletin.rb
/var/www/discourse/config/initializers/013-excon_defaults.rb:4:in `<main>': can't modify frozen Hash: {:chunk_size=>1048576,                                                             :ciphers=>"ECDHE-ECDSA- [................]"

…그리고 이제 문제 해결 능력을 거의 한계에 도달한 것 같습니다.

/var/www/discourse에서 시작하는 것이 차이를 만드는지 확신은 없지만, 저는 항상 그렇게 합니다. OP에서 권장하는 방법은 다음과 같습니다.

그 오류는 알지 못합니다.

1개의 좋아요

어젯밤에 포기할 때까지 몇 시간 동안 '조사’를 한 결과, 제 능력 범위를 벗어난 것들을 너무 많이 만져서 오히려 더 큰 문제를 일으키고 있는 건 아닌지 걱정되었습니다. 또한 누군가가 최근에 이 작업을 수행한 적이 있다면 제가 놓치고 있는 마법 같은 단계가 있는지 알고 싶었습니다. 반복되는 토끼굴에 빠져들기보다는 말이죠. :winking_face_with_tongue:

새로운 문제가 있을 수 있습니다. 며칠 전 mbox 가져오기를 실행했는데, 그 과정에서 에러를 유발한 것과 동일한 코드가 호출된다고 추정됩니다.

아, 저는 컨테이너에서 진행했고 이 설명서는 개발 환경을 위한 것이네요. 개발 환경을 설정하셨나요? 정상적으로 작동하나요? VPN 환경에서는 설정이 까다로울 수 있습니다.

내일 다시 초기화해서 시도해볼 거야. 오늘 밤 두통이 심해서.

결과 알려줄게 :slightly_smiling_face:

1개의 좋아요

개발 환경을 설정했나요? 그게 첫 단계였나요?

호스트 VPS를 통해 시도해 보고, 나중에 컨테이너를 통해 어정쩡하게 처리했습니다. 작업을 마칠쯤에는 어디서 뭘 했는지 헷갈리기 시작했죠. 아마도 제 실력이 부족해서 그런 것 같습니다… 내일 처음부터 다시 시작하겠습니다.

VM에서 작업하는 경우, 컨테이너에서 수행하는 것이 더 쉬울 수 있습니다. 다른 import how-to 예제를 참고해 보세요 (mysql 템플릿이 포함되어 있을 것입니다).

컨테이너에 진입한 후, Gem 파일을 편집하고 bundle install을 실행하게 될 것입니다.

mysql 또는 Mariadb를 위해 컨테이너를 사용하는 것이 합리적일 수 있습니다. (다만, 컨테이너들이 서로를 인식할 수 있도록 해야 합니다)

1개의 좋아요

흥분되는 소식이 있습니다…

스크립트를 실행하는 데 성공했습니다(모든 작업이 끝나면 가이드를 만들 예정입니다). 사용자 그룹은 가져오기 완료되었지만, 사용자 가져오기 단계에서 멈춰 있습니다. 타임존 변수 때문인 것 같습니다.

importing users
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/tzinfo-2.0.6/lib/tzinfo/timestamp.rb:138:in `for': Integer values are not supported (ArgumentError)

            raise ArgumentError, "#{value.class} values are not supported" unless is_time_like?(value)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        from /var/www/discourse/vendor/bundle/ruby/3.3.0/gems/tzinfo-2.0.6/lib/tzinfo/timezone.rb:575:in `utc_to_local'
        from script/import_scripts/vbulletin.rb:1019:in `parse_timestamp'
        from script/import_scripts/vbulletin.rb:166:in `block (2 levels) in import_users'
        from /var/www/discourse/script/import_scripts/base.rb:267:in `block in create_users'
        from /var/www/discourse/script/import_scripts/base.rb:266:in `each'
        from /var/www/discourse/script/import_scripts/base.rb:266:in `create_users'
        from script/import_scripts/vbulletin.rb:148:in `block in import_users'
        from /var/www/discourse/script/import_scripts/base.rb:951:in `block in batches'
        from <internal:kernel>:187:in `loop'
        from /var/www/discourse/script/import_scripts/base.rb:950:in `batches'
        from script/import_scripts/vbulletin.rb:126:in `import_users'
        from script/import_scripts/vbulletin.rb:82:in `execute'
        from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
        from script/import_scripts/vbulletin.rb:1027:in `<main>'

스크립트의 기본값은 "America/Los Angeles"입니다. vBulletin 포럼은 (GMT) Western Europe Time, London, Lisbon, Casablanca)으로 설정되어 있고, 데이터를 가져오는 대상인 Discourse 인스턴스에는 America/Los Angeles와 Europe/Paris 두 항목이 있습니다.

어떤 것을 선택해야 하는지 아시나요?

어떤 것을 선택하든 크게 중요하지 않습니다.

날짜가 스크립트가 기대하는 방식으로 저장되지 않은 것 같습니다.

1개의 좋아요