Last active
December 14, 2017 15:23
-
-
Save YDKK/440428efb1b16dcbdd59d7c45d0c0878 to your computer and use it in GitHub Desktop.
Qiita:Team to Crowi http://ydkk.hateblo.jp/entry/2017/12/14/235727
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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