Migracja systemu komentarzy — utterances

Migracja systemu komentarzy z Disqus do GitHub Issues z użyciem Utterances.
Zawartość

Odkąd zacząłem przygody z własną stroną internetową, ważne było, aby nawiązać swojego rodzaju kontakt z czytelnikiem. System komentarzy był przy tym niezastąpiony.

Zaczynając od wbudowanego systemu w Wordpress, który szybko zalewany był spamem, przeniosłem się na Disqus. Z Disqus zostałem przez cały okres, w tym przeniosłem go podczas rezygnacji z WordPress na rzecz Hugo.

Podczas migracji do Hugo, głównym celem był wygląd, który zadowoli, mnie jak i czytelników, ale również – a co najważniejsze – szybkość.

Dzięki odpowiednim modyfikacją moja strona ładuj się szybko, plasując się na zielono w Google PageSpeed czy też GTMetrix.

Oczywiście, nie wszystkie elementy strony są zoptymalizowane pod kątem prędkości.

Nie chciałem rezygnować z systemu reklam bazującego na Google AdSense, więc podstrony, które wyświetlają reklamy, są nieco wolniejsze od innych. Na to niestety nie mam wpływu.

Na co mam wpływ to system komentarzy.

Disqus nie jest lekki, przez co ograniczyłem jego ładowane do czasu, aż czytelnik zechce zostawić komentarz i kliknie przycisk “Pokaż komentarze”. Pozwoliło mi to zniwelować negatywny wpływ zasobożerności Disqus oraz ograniczyło po części śledzenie użytkowników, które w Disqus jest wszechobecne.

Jako że aspekt prywatności jest obecnie na porządku dziennym, postanowiłem sprawdzić, jaką alternatywę mogę wprowadzić na moją stronę, aby raz na zawsze zrezygnować z Disqus.

Nie zamierzałem korzystać z takich opcji, że po każdym komentarzu moja statyczna strona musi zostać ponownie wygenerowana, aby komentarz był wyświetlony. W związku z tym rozpocząłem poszukiwania.

Poniżej opiszę, jak (po stoczonej walce z samym sobą) przeniosłem komentarze z Disqus do GitHub Issues. Jednocześnie poniższy wpis będzie swojego rodzaju historią zmian na mojej stronie, gdzie zostawię część mojego “starego” kodu z Disqus.

Jak Disqus działa(ł) na mojej stronie

Disqus używałem na mojej stronie w dwojaki sposób. Po pierwsze, przywoływałem disqus count.js, który wyświetlał w nagłówku (i później w przycisku do ładowania komentarzy) ile dany wpis ma komentarzy, w postaci cyfry.

<script id="dsq-count-scr" src="https://dariuszwieckiewicz.disqus.com/count.js" async></script>
<a href="{{ .RelPermalink }}#disqus_thread" class="article-comment-link">{{ T "Comments" }} <span class="disqus-comment-count" data-disqus-url="{{ trim .Permalink "/" }}"></span></a>

Następnie pod tekstem wpisu miałem przycisk, który wyświetlał liczbę komentarzy i na którego można było kliknąć, aby przywołać disqus embed.js w celu załadowania skryptu i wyświetlenia komentarzy zapisanych na serwerach Disqus.

<script>
  var button = document.getElementById("show-comments")
  if (button != null) {

    var clickHandler = function(){
      var disqus_shortname = 'dariuszwieckiewicz';

      var disqus = document.createElement('script');
      disqus.type = 'text/javascript';
      disqus.async = true;
      disqus.src = 'https://' + disqus_shortname + '.disqus.com/embed.js';
      (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(disqus);

      button.style.display = 'none';
  };

  button.addEventListener('click', clickHandler, false);
}
</script>
<div class="disqus-comments">
  <h4>Dołącz do dyskusji</h4>
  <button aria-label="{{ T "ShowComments" }}" id="show-comments" class="btn btn-default" type="button"><span class="disqus-comment-count" data-disqus-url="{{ trim .Permalink "/" }}">{{ T "ShowComments" }}</span></button>
  <div id="disqus_thread"></div>

    <script>
      var disqus_config = function () {
        this.page.url = '{{ trim .Permalink "/" }}';
      };
    </script>

</div>

Metodę na przycisk wprowadziłem z prostego względu, gdyż nie każdy wpis posiadał komentarze, a marnowanie czasu na ładowanie skryptu na każdej stronie miało negatywny wpływ na wydajność całej strony.

Eksport komentarzy z Disqus

Jako że nie chciałem stracić obecnych komentarzy, najpierw musiałem wyeksportować je z Disqus to pliku.

W tym celu zalogowałem się na moje konto w Disqus, a następnie przeszłem na stronę administracyjną skąd wybrałem moją stronę internetową.

Przechodząc do zakładki Moderation, po lewej stronie mamy dostępne menu Export, w którym to klikając na przycisk Export Comments możemy zlecić wykonanie kopii zapasowej.

Disqus Moderation Export

Eksport nie następuje natychmiastowo, dlatego powita nas wiadomość:

Your export has been queued. We will send an email to your registered address when it is available.

W zależności od tego, jak dużo mamy komentarzy, po chwili (dłuższej lub krótszej) otrzymamy maila, że nasz eksport jest gotowy do pobrania, wraz z odnośnikiem do pobrania pliku XML spakowanego w GZip.

I tak mam plik z komentarzami, który muszę przerobić i z importować do mojego repozytorium na GitHub. W tym celu utworzyłem repozytorium o nazwie mojej strony idarek/dariusz.wieckiewicz.org.

Rozpakowujemy plik i tak otrzymujemy nasz plik XML.

Poszukiwania metody importu komentarzy do GitHub Issues

W swoich poszukiwaniach natrafiłem na dwa przeplatające się wpisy, Removing Disqus and adding GitHub Issue Comments) oraz Migrate Disqus comments to Utterances (in GitHub issues) with Python.

Autorzy obu stron przedstawili różne metody na przeniesienie komentarzy. Jürgen wykorzystał .NET, natomiast Pawamoy skrypt w Python.

Chciałem skupić się na wpisie Jürgena, jednak jako użytkownika macOS (mimo że Windows mam pod ręką wirtualnie) nie chciałem używać narzędzia działającego głównie pod jednym systemem (Windows), mimo dostępnego pakietu .NET na macOS, więc poszukałem innego rozwiązania. To rozwiązanie znalazłem na stronie pawamoy, który wykorzystuje skrypt Python.

Importowanie komentarzy — podejście pierwsze

Rozwiązanie od Pawamoy wydałało się prostrze w użyciu (pozornie).

W pierwszej kolejności należało wygenerować token dostępowy do GitHub z opcją public_repo.

Generate Token GitHub

Tak wygenerowany token należałoby wkleić w konfigurację parametrów środowiskowych dla dalszego skryptu python. Dostosowałem również nazwą użytkownika, repozytorium oraz adres mojej strony.

Wyglądałoby to mniej więcej tak:

export FILEPATH="comments.xml"
export USERNAME="idarek"
export TOKEN="naszWygenerowanyToken"
export REPOSITORY="idarek/dariusz.wieckiewicz.org"
export BASE_URL="https://dariusz.wieckiewicz.org/"

W dalszej części należałoby skopiować główny skrypt (nieco go dostosowując — spolszczając) i uruchomić.

Nim to jeszcze zrobiłem, doczytałem i otrzymałem potwierdzenie od autora skryptu, że aby wszystko działało, Utterances musi już działać, a na każdym wpisie, na którym był komentarz w Disqus, należy napisać komentarz testowy, aby Utterance utworzył wstępny GitHub Issue. W przeciwnym razie nie będzie to działać.

Jeżeli w przypadku niewielkiej liczby wpisów to może nie jest problem, ale wyobraźcie sobie robienie tego przy setkach wpisów i komentarzy. Pomyślałem więc, że odpuszczę.

W związku z tym, nim podążyłem tą drogą, wróciłem do wpisu Jürgen Gutsch, który opisał nieco inne podejście do procesu migracji.

Jürgen zamiast pythona wykorzystał skrypt w C#, w związku z tym, uruchomienie go w innym środowisku niż Windows może być bardziej skomplikowane. Na szczęście Microsoft udostępnił .NET Framework oraz .NET Core również dla innych systemów operacyjnych, więc była nadzieja!

Importowanie komentarzy — podejście drugie

Utterances pozwala powiązać wpis na stronie z komentarzem (GitHub Issue) między innymi przy pomocy tytułu strony (title), pełnego adresu (url) lub adresu skróconego, bez przedrostka domeny (pathname).

Dla mnie pathname był tym, czego szukałem, wówczas tytuł GitHub Issue wyglądałby następująco ‌/przywracanie-grub-boot-loadera-po-instalacji-windowsa/.

Przeanalizowałem wpis Jürgena i zauważyłem, że skoncentrował on się na GitHub Issue bazujące na Tytule wpisu (title).

Przyznając się szczerze, nie jestem programistą i od samego patrzenia na wpis Jürgena stwierdziłem, że jest to nieco za dużo na moje doświadczenie (trzeba znać swoje limity i wiedzieć, kiedy powiedzieć pass). W związku z tym zacząłem moje dalsze poszukiwania.

Oczywiście, jeżeli czujesz się mocny w tej dziedzinie, warto spróbować. W przeciwieństwie do pierwszego podejścia skrypt Jürgena nie potrzebuje (jeszcze) działającego Utterances na stronie. W ramach procesu importu utworzy on niezbędne GitHub Issues, które później będzie można wykorzystać z Utterances.

Importowanie komentarzy — podejście trzecie

I tak natrafiłem na repozytorium harttle/disqus2github na GitHub.

Całość wyglądała bardzo prosto i przejrzyście. Sprawdzając, jak autor z importował wpisy do swojej strony wszystko wyglądało tak, jak bym chciał.

Oczywiście, pamiętałem o dostosowaniu (spolszczeniu) fraz przed importem, oraz problem, na który natrafili zarówno Jürgen jak i Pawamoy skutkujący konieczności dodania opóźnienie kilkusekundowego do procedury importu, aby nie zostać zbanowanym przez system GitHub.

A więc pobrałem repozytorium harttle/disqus2github. Po czym otworzyłem w Atom w celu dostosowania.

Disqus2GitHub Atom

Rozpocząłem od zmiany w config.example.json wprowadzając moje repozytorium oraz token, który wygenerowaliśmy wcześniej.

{
  "repo": "idarek/dariusz.wieckiewicz.org",
  "xmlFile": "./data.xml",
  "token": "naszWygenerowanyToken"
}

Następnie zmieniłem nazwę pliku z config.example.json na config.json.

Nasze wyeksportowane komentarze z Disqus (*-all.xml.gz) rozpakowałem i zmieniłem nazwę na data.xml.

W pliku github.js zmieniłem linijkę 8 z:

body: comment.message + '\n\nBy ' + comment.author + ' ' + comment.date

na:

body: comment.message + '\n\nPrzez ' + comment.author + ' ' + comment.date

Pora na instalację niezbędnych modułów oraz wykonanie importu.

npm install node index.js

Oczywiście, musimy mieć node zainstalowany w systemie

brew install node

Nie wiesz, czym jest Brew? Jest to menadżer pakietów, który rozszerza znacząco możliwości system macOS (osobiście przypomina mi apt z systemu Debian/Ubuntu).

Niestety, nie wszystko poszło po myśli, a konsola wyrzuciła tonę błędów, a import nie nastąpił.

Problem okazał się node-expat oraz xml2json.

Napisałem do Autora (Jun Yang) z zapytaniem, ale otrzymana odpowiedź zniechęciła mnie do dalszej zabawy z tym rozwiązaniem.

“(…) Try upgrade it or replace with another xml parser. This repo is meant to be a scaffold instead of a versatile cli tool. Try tweak it around and if you find it useful for everyone (…)”

No nic, dalsze poszukiwania trzeba zacząć.

Importowanie komentarzy — podejście czwarte

I tak w moich dalszych poszukiwaniach natrafiłem na stronę François-Xavier Cat (przyznam szczerze, że przeplotła się ona wcześniej przez moje okno przeglądarki). Na swoim wpisie zatytułowanym Moving my blog comments from Disqus to Github issues using PowerShell opisał on metodę na wykorzystanie PowerShell do importu komentarzy.

Jako że PowerShell nie jest mi obcy, gdyż jest wszechobecny w Windows 10 oraz można go zainstalować w macOS, postanowiłem spróbować.

W tym celu użyłem Homebrew (Brew), tak jak wspomniałem wcześniej.

brew install powershell

I tak z poziomu terminala mamy dostępną komendę pwsh w celu uruchomienia PowerShell.

François-Xavier Cat odwalił kawał dobrej roboty, opisując dlaczego zdecydował się na rezygnacje z Disqus, przedstawiając plusy i minusy całego zamieszania.

W moim podejściu postanowiłem wykorzystać ten sam plan, który wstępnie autor założył, pomijając eksport komentarzy, który dokonałem na samym początku.

Nim zacząłem poniższe działania, otworzyłem mój plik exportu XML w Atom i zmieniłem wszystkie odnośniki http:// na https:// gdyż moja strona serwowana jest wyłącznie przez bezpieczne połączenie z zieloną kłódką. Było tego w sumie 272 pozycje, a Atom poradził sobie w sekundę :).

Uruchomiłem pwsh i załadowałem komentarze następującą komendą.

# Load the file
$Disqus = Get-Content -Path ./dariuszwieckiewicz-all.xml

# Cast the file to XML format
$DisqusXML = ([xml]$Disqus).disqus

# Output result
$DisqusXML

Na etapie wyjściowym (output) otrzymałem:

xmlns          : http://disqus.com
dsq            : http://disqus.com/disqus-internals
xsi            : http://www.w3.org/2001/XMLSchema-instance
schemaLocation : http://disqus.com/api/schemas/1.0/disqus.xsd http://disqus.com/api/schemas/1.0/disqus-internals.xsd
category       : category
thread         : {thread, thread, thread, thread…}
post           : {post, post, post, post…}

Eksport z Disqus nie jest przyjazny użytkownikowi. Wpisy i komentarze porozrzucane są po całym pliku i powiązane przez thread, posts oraz unikalny identyfikator (ID).

W pierwszej kolejności autor skupił się na odpowiednim obrobieniu informacji, zaczynając od samych komentarzy.

# Retrieve all Comments
$AllComments = $DisqusXML.post

# Retrieve properties available for each comments
$Properties = $AllComments | Get-Member -MemberType Property

# Process each Comments
$AllComments | Foreach-Object -process {

  # Store the current comment
  $Comment = $_

  # Create Hashtable to store properties of the current comment
  $Post = @{}

  # Go through each properties of each comments
  foreach ($prop in $Properties.name)
  {
     if($prop -eq 'id')
     {
        # Capture Unique IDs
        $Post.DsqID = $Comment.id[0]
        $Post.ID = $Comment.id[1]
     }
     elseif($prop -eq 'author')
     {
        # Author information
        $Post.AuthorName = $Comment.author.name
        $Post.AuthorIsAnonymous = $Comment.author.isanonymous
     }
     elseif($prop -eq 'thread')
     {
        # Here is the important data about the
        #  thread the comment belong to
        $Post.ThreadId = $Comment.thread.id
     }
     elseif($prop -eq 'message')
     {
        $Post.Message = $Comment.message.'#cdata-section'
     }
     else{
        # Other properties
        $Post.$prop = ($Comment |
            Select-Object -ExpandProperty $prop ) -replace '`r`n'
     }
     # Keep the original comment data structure if we need it later
     $Post.raw = $Comment
  }
  # Return a PowerShell object for the current comment
  New-Object -TypeName PSObject -Property $Post
}

Aby zobaczyć, jak wszystko wygląda wykonujemy komendę $DisqusXML.post

Następnie, to samo dla wątków (wpisów).

# Retrieve threads
$AllThreads = $DisqusXML.thread

# Retrieve Thread properties
$Properties = $AllThreads |
    Get-Member -MemberType Property

# Process each threads
$AllThreads=$AllThreads | Foreach-Object -process {

    # Capture Current ThreadItem
    $ThreadItem = $_

    # Create Hashtable for our final object
    $ThreadObj = @{}

    # Go through each properties of each threads
    foreach ($prop in $Properties.name)
    {
        if($prop -eq 'id')
        {
            # Thread ID
            $ThreadObj.ID = $ThreadItem.id[0]
        }
        elseif($prop -eq 'author')
        {
            # Author
            $ThreadObj.AuthorName = $ThreadItem.author.name
            $ThreadObj.AuthorIsAnonymous = $ThreadItem.author.isanonymous
            $ThreadObj.AuthorUsername = $ThreadItem.author.username
        }
        elseif($prop -eq 'message')
        {
            $ThreadObj.Message = $ThreadItem.message.'#cdata-section'
        }
        elseif($prop -eq 'category')
        {
            $ThreadObj.Category = ($ThreadItem|
                Select-Object -ExpandProperty $prop).id
        }
        else{
            # Other properties
            $ThreadObj.$prop = ($ThreadItem |
                Select-Object -ExpandProperty $prop) -replace '`r`n'
        }
        $ThreadObj.raw = $ThreadItem
    }
    # Return a PowerShell object for the current ThreadItem
    New-Object -TypeName PSObject -Property $ThreadObj
}

Aby zobaczyć, jak wszystko wygląda wykonujemy komendę $DisqusXML.thread

Po tym kroku autor wspomniał o śmieciach, które nagromadziły się w jego systemie komentarzy na przełomie lat oraz podczas używania przez niego różnych platform do publikacji jego twórczości.

Osobiście pominąłem ten krok, gdyż nie miałem z tym problemu. Odkąd zacząłem korzystać z Disqus, moja strona zagnieździła się na dobre z obecnym adresem strony.

Nim autor zdecydował się na połączenie wpisów z komentarzami, postanowił wprowadzić procedurę sprawdzającą tytuł wpisu z aktualnie działającą stroną. Miało to na celu uzupełnienie ewentualnych braków w eksporcie z Disqus (również to pominąłem w moim imporcie).

Dodatkowo postanowił usunąć nieco śmieci z tytułów wpisów oraz pozostawić w odnośniku do pliku tylko pathname.

# Create 2 properties 'link2' and 'title2' that will contains the clean information
#  then group per link (per thread or post)
#  link2: trimed URL
#  title2: Remove prefix, weird brackets or weird characters, Encoding issues
$AllThreads = $threads|
  Select-Object -Property *,
  @{L='link2';E={
      $_.link -replace "
        https://|http://|www\.|
        lazywinadmin\.com|
        lazywinadmin\.github\.io|
        \/minimal-mistakes|
        \/powershell"}},
  @{L='title2';E={
      $_.title -replace "^LazyWinAdmin:\s" `
        -replace '’',"'" `
        -replace "…|…" `
        -replace '–','-' `
        -replace '\/\[','[' `
        -replace '\\\/\]',']' }}|
  Group-Object -Property link2

W moim przypadku wyglądałoby to następująco.

$AllThreads = $threads|
  Select-Object -Property *,
  @{L='link2';E={
      $_.link -replace "
        https://|http://|www\.|
        dariusz\.wieckiewicz\.org"}},
  @{L='title2';E={
      $_.title
        -replace " // Dariusz Więckiewicz","" `
        -replace "&#8230;","..." `
        -replace "&#8222;",'"' `
        -replace "&#8221;",'"' `
        -replace "&#8211;",'-' }}|
  Group-Object -Property link2

Pora na połączenie komentarzy ze wpisami.

# Append the thread information to the each comment object
# Here we just pass the realtitle and link2
$AllTogether = $Comments | ForEach-Object -Process {
    $CommentItem = $_
    $ThreadInformation = $ThreadsUpdated |
        Where-Object -FilterScript {
            $_.id -match $CommentItem.ThreadId
        }

    $CommentItem |
        Select-Object -Property *,
            @{L='ThreadTitle';E={$ThreadInformation.title2}},
            @{L='ThreadLink';E={$ThreadInformation.link2}}
} |
Group-Object -Property ThreadLink |
Where-Object -FilterScript {$_.name}

Jeżeli wszystko przeszło poprawnie, gotowi jesteśmy na utworzenie GitHub Issue oraz dodanie naszych komentarzy.

W tym celu niezbędne będzie doinstalowanie modułu PowerShellForGithub do PowerShell oraz dodanie naszych danych uwierzytelniających.

# First fetch the module from the PowerShell Gallery
Install-Module -Name powershellforgithub -scope currentuser -verbose
# Import it
Import-Module -Name powershellforgithub

# Specify our Github Token
$key = 'naszWygenerowanyToken'
$KeySec = ConvertTo-SecureString $key -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ('username_is_ignored', $KeySec)

# Set Connection and configuration
Set-GitHubAuthentication -Credential $cred
Set-GitHubConfiguration -DisableLogging -DisableTelemetry

Zmieniamy tylko naszWygenerowanyToken na Token API, który wygenerowaliśmy na początku tego wpisu.

Teraz jesteśmy gotowi na import, dokonując nieco dostosowania do naszego środowiska (OwnerName, RepositoryName, BlogUrl) oraz spolszczając co nieco.

# Define Github commands default params
$GithubSplat = @{
    OwnerName = 'idarek'
    RepositoryName = 'dariusz.wieckiewicz.org'
}
$BlogUrl = 'https://dariusz.wieckiewicz.org'

# Retrieve issues
$issues = Get-GitHubIssue @githubsplat

# Process each threads with their comments
$AllTogether |
Sort-Object name -Descending |
ForEach-Object -Process{

    # Capture current thread
    $BlogPost = $_

    # Issue Title, replace the first / and
    #  remove the html at the end of the name
    $IssueTitle = $BlogPost.name -replace '^\/' -replace '\.html'

    # lookup for existing issue
    $IssueObject = $issues|
        Where-Object -filterscript {$_.title -eq $IssueTitle}

    if(-not $IssueObject)
    {
        # Build Header of the post
        $IssueHeader = $BlogPost.group.ThreadTitle |
          select-object -first 1

        # Define blog post link
        $BlogPostLink = "$($BlogUrl)$($BlogPost.name)"

        # Define body of the issue
        $Body = @"
# $IssueHeader

[$BlogPostLink]($BlogPostLink)

<!--
Imported via PowerShell on $(Get-Date -Format o)
-->
"@
        # Create an issue
        $IssueObject = New-GitHubIssue @githubsplat `
            -Title $IssueTitle `
            -Body $body `
            -Label 'blog comments'
    }

    # Sort comment by createdAt
    $BlogPost.group|
      Where-Object {$_.isspam -like '*false*'} |
      Sort-Object createdAt |
      ForEach-Object{

        # Current comment
        $CurrenComment = $_

        # Define body of the comment
        $CommentBody = @"
**Dodano**: ``$($CurrenComment.createdAt)``
$($CurrenComment.message)

<!--
Imported via PowerShell on $(Get-Date -Format o)
Json_original_message:
$($CurrenComment|Select-Object -ExcludeProperty raw|convertTo-Json)
-->
"@
        # Create Comment
        New-GitHubComment @githubsplat `
            -Issue $IssueObject.number `
            -Body $CommentBody
    }
    # Close issue
    Update-GitHubIssue @githubsplat `
        -Issue $IssueObject.number `
        -State Closed
}

I co, i nic! W moim przypadku nie zadziałało. Po raz kolejny, moje doświadczenie jest niewystarczające, żeby znaleźć przyczynę.

Kolejne rozwiązanie (mimo że prawdopodobnie poprawne) do śmieci a ja wróciłem do punktu wyjścia.

Importowanie komentarzy — podejście piąte

Jako że liczba działających sposobów poniekąd się wyczerpała, pozostała ostatnia metoda — manualna, tak jak opisał to Nan Xiao w punkcie 2 procesu migracji.

To skoro już zamierzam zrobić to ręcznie, to dlaczego by nie wrócić do podejścia pierwszego od pawamoy i jego półautomatycznego importu.

Przypominając.

Aby podejście pierwsze zadziałało, Utterances musi już działać, w związku z tym skoczymy najpierw nieco do przodu.

Nie kontynuuj czytania poniżej — przejdź (kliknij) do przodu. Na dalszy etapie wrócisz do poniższego paragrafu. W przeciwnym razie będzie ci się wszystko wydawało oderwane od rzeczywistości.


Dalej

Skoro mamy już aktywny i działający system komentarzy, pora na import z Disqus.

Żeby dowiedzieć się, które wpisy posiadają komentarze, wykorzystałem do tego Terminal macOS.

more dariuszwieckiewicz-all.xml | grep "thread dsq:id" | grep ">"

Dzięki temu otrzymałem bogatą listę:

(...)
<thread dsq:id="6892197256">
<thread dsq:id="6892197256">
<thread dsq:id="6892178219">
<thread dsq:id="6892178219">
<thread dsq:id="7905495271">
<thread dsq:id="7905495271">
<thread dsq:id="7905495271">
<thread dsq:id="6892197256">
<thread dsq:id="6892197256">
(...)

Tylko 181 odnośników 😱

Na szczęście sporo z nich się powtarza, świadcząc o kilku komentarzach w tym samym wpisie.

Posortowałem i usunąłem duplikaty i tak zostało mi 41 wpisów z komentarzami.

Teraz, przeszukując mój plik XML, musiałem znaleźć URL odpowiadający otrzymanemu numerowi.

Irytujące stało się to, że Disqus powiązał komentarze z adresami URL, które miałem lata temu, jeszcze przed pozbyciem się daty z URL, podczas gdy teraz wszystkie moje wpisy posiadają pojedynczy pathname.

Na szczęście dotyczyło ty tylko 11 wpisów sprzed 2014 roku, które mogłem zmienić ręcznie.

Dzięki temu otrzymałem 33 wpisy z komentarzami, na których musiałem napisać test aby zainicjować GitHub Issue i umożliwić mi późniejszy import metodą pawamoy.

W przypadku Hugo można to było zrobić, uruchamiając go lokalnie lub zdecydować się na pracę Online. Osobiście sprawdziłem wszystko online, aby upewnić się, że nie robię nic na darmo, a wszystko działa tak jak trzeba.

Nim jednak zacząłem, doinstalowałem python3 za pomocą brew install python3 w moim systemie oraz zaktualizowałem pip3.

sudo -H pip3 install --upgrade pip --force-reinstall

Następnie zainstalowałem niezbędne pakiety dodatkowe PyGithub oraz xmltodict (bez konieczności ich pobierania).

pip3 install PyGithub
pip3 install xmltodict

W terminalu wykonujemy poniższe komendy w celu ustawienia środowiska startowego:

export FILEPATH="dariuszwieckiewicz-all.xml"
export USERNAME="idarek"
export TOKEN="naszWygenerowanyToken"
export REPOSITORY="idarek/dariusz.wieckiewicz.org"
export BASE_URL="https://dariusz.wieckiewicz.org/"

Następnie przygotowujemy plik disqus.py wraz z poniższym skryptem odpowiadający za import.

Od siebie dodałem import os gdyż w macOS nie chciało bez tego działać.

import time
import os
import xmltodict
from github import Github

FILEPATH = os.environ["FILEPATH"]
USERNAME = os.environ["USERNAME"]
TOKEN = os.environ["TOKEN"]
REPOSITORY = os.environ["REPOSITORY"]
BASE_URL = os.environ["BASE_URL"]


def disqus_to_github():
    g = Github(TOKEN)
    repo = g.get_repo(REPOSITORY)
    issues = repo.get_issues()

    with open(FILEPATH) as fd:
        data = xmltodict.parse(fd.read())

    data = data["disqus"]

    threads = [dict(t) for t in data["thread"]]
    posts = sorted((dict(p) for p in data["post"]), key=lambda d: d["createdAt"])

    # only keep threads with comments
    twc_ids = set(p["thread"]["@dsq:id"] for p in posts)
    threads = {t["@dsq:id"]: t for t in threads if t["@dsq:id"] in twc_ids}

    # associate the thread to each post
    for post in posts:
        post["thread"] = threads[post["thread"]["@dsq:id"]]

    # associate the related GitHub issue to each thread
    # warning: the issues need to exist before you run this script!
    # write a "test" comment in each one of your post with comments
    # to make Utterances create the initial issues
    for thread in threads.values():
        for issue in issues:
            if issue.title == thread["link"].replace(BASE_URL, ""):
                thread["issue"] = issue
                break

    # iterate on posts and create issues comments accordingly
    for i, post in enumerate(posts, 1):
        name = post["author"]["name"]
        user = post["author"].get("username")
        mention = " @" + user if user and not user.startswith("disqus_") else ""
        date = post["createdAt"]
        message = post["message"]
        issue = post["thread"]["issue"]
        body = f"*Oryginalna data: {date}*\n\n{message}"
        # don't add original author when it's you
        if user != USERNAME:
            body = f"*Oryginalny autor:* **{name}{mention}**  \n{body}"
        print(f"Posting {i}/{len(posts)} to issue {issue.number}    \r", end="")
        issue.create_comment(body)
        # prevent hitting rate limits!
        time.sleep(0.5)

    print()


if __name__ == "__main__":
    disqus_to_github()

Zakładając, że na wszystkie 33 wpisach dodałem testowy komentarz pora na uruchomienie naszego skryptu.

W macOS, z zainstalowany python3 za pomocą brew wykonałem to następująco:

FILEPATH="dariuszwieckiewicz-all.xml" USERNAME="idarek" TOKEN="naszWygenerowanyToken" REPOSITORY="idarek/dariusz.wieckiewicz.org" BASE_URL="https://dariusz.wieckiewicz.org" python3 disqus.py

Przy pierwszym uruchomieniu natrafiłem jednak na problem, który mnie nieco zirytował.

Traceback (most recent call last):
  File "disqus.py", line 74, in <module>
    disqus_to_github()
  File "disqus.py", line 52, in disqus_to_github
    issue = post["thread"]["issue"]
KeyError: 'issue'

Okazało się, że moje obecne środowisko domyślnie usuwa / z końca adresu URL natomiast eksport z Disqus posiadał / w każdym linku. Raz jeszcze musiałem użyć Atom, aby hurtowo to zmienić (/</link> na </link>).

I tak po ponownym uruchomieniu wszystko zaczęło działać. Po dłuższej chwili ujrzałem:

Posting 181/181 to issue 11

Pora na sprawdzenie.

Disqus2GitHub Imported

Yuppi! Wszystko zostało z importowane.

I mimo tego, że wygląda to poniekąd dziwnie (wszystkie komentarze z importowane napisane przez mnie), to inaczej się nie dało.

Teraz wystarczy usunąć wpisy “test” z każdego z GitHub Issue i przejrzeć, czy nie importował się jakiś spam :)

Przejdź do zakończenia.


Utterances

Utterance jest lekkim systemem komentarzy bazujący na GitHub Issues, a dokładniej A lightweight comments widget built on GitHub issues.

Repozytorium

Nim zaczniemy wstawiać Utternaces na naszą stronę, musimy utworzyć repozytorium które wykorzystamy do przechowywania komentarzy.

Po zalogowaniu się na GitHub klikamy przycisk New przy repozytoriach.

Repositories NEW

Następnie wybieramy nazwę i przyznajemy publiczny dostęp (public_repo).

GitHub Create a new repository

Na moje repozytorium wybrałem idarek/dariusz.wieckiewicz.org.

Dostęp aplikacji Utterances

Przechodzimy na stronę aplikacji (GitHub App) Utterances, aby skonfigurować dostęp do naszego repozytorium.

GitHub App Utterances Config

Potwierdzamy przyznanie prawa do odczytu metadanych oraz odczytu i zapisu GitHub Issues.

Następnie wybieramy i dodajemy, do którego repozytorium aplikacja będzie miała dostęp.

Utterances Configuration

Generowanie kodu na stronę

Wracając na stronę główną utteranc.es przechodzimy do sekcji pola repo: gdzie wpisujemy nasze repozytorium, a następnie do Blog Posts <> Issue Mapping.

Tutaj wybieramy, w jaki sposób GitHub Issues będą tworzone.

Jeżeli czytasz od początku, osobiście zależało mi, aby tytuł GitHub Issue był odpowiednikiem pathname. Jest to pierwsza, domyślna opcja przy konfiguracji ze strony Utterances.

Utterances Blog Post Issue Mapping

Następnie wybieramy, jaką etykietę każdy z naszych GitHub Issues będzie miał przypisaną.

Dzięki temu możemy pogrupować GitHub Issues utworzone przez naszą stronę oraz bezpośrednio z GitHub. Jest to szczególnie przydatne, jeżeli dane repozytorium używamy do więcej niż do celów komentarzy.

Utterances Issue Label

Następnie wybieramy motyw. W moim przypadku, jako że mam wprowadzony tryb jasny i ciemny strony, który zmienia się automatycznie (oraz za pomocą ikony na górze strony) wybrałem Preferred Color Scheme.

Utterances Theme

I tak doszliśmy do kodu, który umożliwi nam dodanie systemu komentarzy bazującego na Utterances na naszej stronie.

Utterances Enable

W moim przypadku kod końcowy wyglądał następująco:

<script defer src="https://utteranc.es/client.js"
        repo="idarek/dariusz.wieckiewicz.org"
        issue-term="pathname"
        label="comment"
        theme="preferred-color-scheme"
        crossorigin="anonymous"
        async>
</script>

Od siebie dodałem słowo defer w celu opóźnienia ładowania skryptu do momentu, gdy jest on potrzebny.

Oprócz samego jego dodania pozostaje nam odpowiednie dostosowanie za pomocą CSS oraz .utterances i .utterances-frame.

Jeżeli kontynuujesz czytanie z piątego podejścia, klikamy tutaj i wracamy do czytania.


Na koniec

Cel osiągnięty!

Mimo tego, że próbowałem przeróżnych metod, w związku z moimi ograniczeniami programistycznym (nie jestem programistą) udało mi się dojść do końca.

Przyznam szczerze, że w pewnym momencie chciałem dać za wygraną i darować sobie import, a nawet usunięcie całego powyższego wpisu, na który poświęciłem sporo czasu.

Czasami przerwa pomaga w przemyśleniu, dzięki czemu mogę napisać teraz… sukces!

Nowy system komentarzy możecie podziwiać poniżej.

Pozdrawiam.

Komentarze
Kategorie