Skip to content

Instantly share code, notes, and snippets.

@YDKK
Last active December 14, 2017 15:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save YDKK/440428efb1b16dcbdd59d7c45d0c0878 to your computer and use it in GitHub Desktop.
Save YDKK/440428efb1b16dcbdd59d7c45d0c0878 to your computer and use it in GitHub Desktop.
//
// Qiita2Crowi.cs
//
// Created by YDKK on 2017/12/14.
// Copyright © 2017 YDKK. Licensed under MIT.
//
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Codeplex.Data;
using System.Numerics;
namespace Qiita2Crowi
{
class Program
{
const string accessToken = "CrowiへのAccessToken";
static async Task Main(string[] args)
{
const string titlePrefix = "/qiita/";//Crowiで作成する記事のPrefix
const string baseUrl = "https://crowi.example.jp";//CrowiのUrl
var qiitaJson = File.ReadAllText(args[0]);
var qiita = DynamicJson.Parse(qiitaJson);
var r = new Regex("<img.*src=\"(https?://.*?)\"");
var client = new HttpClient(new HttpClientHandler { UseCookies = false });
client.DefaultRequestHeaders.Add("Cookie", "secure_token=hoge; user_session_key=fuga");//QiitaのセッションCookie
var createdTitleList = new List<string>();
int retryCnt;
foreach (dynamic article in (object[])qiita.articles)
{
retryCnt = 1;
var titleSuffix = "";
createdTitleList.Add(titlePrefix + article.user.id + "/" + ReplaceTitle(article.title));
retry:
//まずページを作る
var param = new Dictionary<string, string>
{
{ "access_token", accessToken },
{ "body", article.body },
{ "path", titlePrefix + article.user.id + "/" + ReplaceTitle(article.title) + titleSuffix }
};
var content = new FormUrlEncodedContent(param);
var result = await client.PostAsync($"{baseUrl}/_api/pages.create", content);
var res = DynamicJson.Parse(await result.Content.ReadAsStringAsync());
if (res.ok != true)
{
Console.WriteLine("[ERROR] Failed to create page.");
Console.WriteLine("Title: " + article.title);
if (createdTitleList.Count(x => x == titlePrefix + article.user.id + "/" + ReplaceTitle(article.title)) >= 2)
{
Console.WriteLine("Change title and retry.");
titleSuffix = $"{++retryCnt}";
goto retry;
}
continue;
}
var createdPageId = res.page.id;
var createdPagePath = res.page.path;
//ページ中の画像を抽出して添付する
var mc = r.Matches((string)article.rendered_body);
var newBody = (string)article.body;
foreach (Match match in mc)
{
var imageUrl = match.Groups[1].Value;
var response = await client.GetAsync(imageUrl);
var image = await response.Content.ReadAsByteArrayAsync();
var attachedImageUrl = await Upload(createdPageId, imageUrl.Split('/').Last(), image);
newBody = newBody.Replace(imageUrl, attachedImageUrl);
}
//コメントも移行する
if (mc.Count > 0)
{
//添付した画像のURLで古い画像のURLを置き換える
param = new Dictionary<string, string>
{
{ "access_token", accessToken },
{ "body", newBody },
{ "page_id", createdPageId }
};
content = new FormUrlEncodedContent(param);
result = await client.PostAsync($"{baseUrl}/_api/pages.update", content);
res = DynamicJson.Parse(await result.Content.ReadAsStringAsync());
if (res.ok != true)
{
Console.WriteLine("[ERROR] Failed to update page.");
Console.WriteLine("Path: " + createdPagePath);
continue;
}
}
if (article.comments_count > 0)
{
foreach (dynamic comment in (object[])article.comments)
{
param = new Dictionary<string, string>
{
{ "access_token", accessToken },
{ "commentForm[page_id]", createdPageId },
{ "commentForm[revision_id]", (BigInteger.Parse(createdPageId, NumberStyles.HexNumber) + 1).ToString("X") },
{ "commentForm[comment]", $"{comment.user.id} commented at {comment.updated_at}\n{comment.body}" }
};
content = new FormUrlEncodedContent(param);
result = await client.PostAsync($"{baseUrl}/_api/comments.add", content);
res = DynamicJson.Parse(await result.Content.ReadAsStringAsync());
}
}
}
}
public static async Task<string> Upload(string pageId, string filename, byte[] image)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.ExpectContinue = false;
using (var content = new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture)))
{
content.Add(new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes(accessToken))), "access_token");
content.Add(new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes(pageId))), "page_id");
content.Add(new StreamContent(new MemoryStream(image)), "file", filename);
var message = await client.PostAsync($"{baseUrl}/_api/attachments.add", content);
var input = await message.Content.ReadAsStringAsync();
return !string.IsNullOrWhiteSpace(input) ? Regex.Match(input, $@"{baseUrl.Replace(".", "\\.")}/files/[0-9a-f]*").Value : null;
}
}
}
private static string ReplaceTitle(string title)
{
return title
.Replace('^', '^')
.Replace('$', '$')
.Replace('*', '*')
.Replace('%', '%')
.Replace('?', '?')
.Replace('/', '/');
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment