はじめに
俺受験生ですし、本当はアドベントカレンダー書くつもり無かったんですけど、なんか書きたくなったので書きます(大丈夫か)
昨年はクソアプリアドベントカレンダーに1個、書いたんですけど、今年はまともなもん作っちゃったんですよ。それで、今年1年の開発のまとめというか、そんな感じで書いてみようかな、と。
本当はMisskeyのアドベントカレンダーやりたかったんですけど、まあ思い立つのが遅すぎて埋まってましたよねはい。
名乗り遅れました、あるかっぱ/アルパカ本家 と申します。各種ウェブサイトをMisskey(Mastodonと同様Activity Pubを利用した分散型SNS)にシェアするためのブラウザ拡張機能、Twishare to Misskey を開発しております。インストールや使い方などはこちらのリンク先に記載しておりますのでとりあえず使いたいのーーー!!って人はご覧ください。
この記事では開発動機から、動作の仕組み、とかなんか色々書いていきたいと思います。書いたあと読み返しとか軽くしかしない人なので1ミスってたらすいません
れっつごー
動機
僕自身、Misskeyに登録したのはTwitterAPI大騒動(2023.3)頃だったと思います。わざわざプロフィール見に行くのめんどくさいので正確な日付はわからん2
Misskeyのアカウントを作成して1ヶ月ほどたった2023年4月、WebページをMisskeyにシェアする手段がほぼないことに気づきました。
ただ、(他の開発者の方々を批判する意図はありませんが)他のブラウザ拡張はアクセストークンを平文で保存しているものが多かったため、ちょっとセキュリティ的に心配かもしれないな、とは思っていました。
そこで、それ以前にMisskey Hub3
を見ていたところ、共有フォームという機能があったのを思い出したんです。
共有フォームとは、https://(Misskeyインスタンスドメイン)/share
にクエリパラメータでtext
やtitle
、url
などを渡すことで外部からユーザーに投稿を促すことができるようなページです。Twitterで言うところのhttps://twitter.com/intent/tweet
ですね。
現在は主に、未来情報産業株式会社さんのMisskey Share (診断メーカーに実装されたことで話題になったヤツ)で利用されています。
また、多くのウェブサイトにはTwitterへのシェアボタンが実装されているのでそれを活用できないかと考えました。
僕自身、ストアには公開しませんでしたが、以前ちょっとだけブラウザ拡張作ったことがあったので作ってみようかなぁと。
制作の歴史(?)と実装について
v0.0.x系統
現在はTwishare to Misskeyとして、/shareが存在する全てのサーバーへのシェアを提供していますが、もともとはTwishare to ioという、Misskey.ioのみにシェアするものでした。
Twishare to ioは、UIはほぼなく、単にtwitterシェアボタンをジャックするだけでした。
具体的には、拡張機能のmanifest.jsonにおいて、https://twitter.com/intent/tweet のページが開かれた時にロード後確認画面を出して、URLに渡されたパラメータを多少整形。それから https://misskey.io/share に渡す、って感じです。
これを公開したところ、なんかめちゃくちゃ偉業4いただいて、なんか、めちゃくちゃテンション爆アゲで。
(ちなみに、Chromeウェブストアのデベロッパー登録時に$5をGoogleに納めなきゃ行けないんですが、当時の相場で620円くらいだったのになんか端数切り上げられて700円取られました。金欠男子高校生の80円を返してくれGoogle)
v0.1.x系統
通知破壊5を眺めていたら、現在は僕が主に生息しているサーバーであるもこきー の管理人である、たこさん@emtk@mkkey.net (当時は面識なかった)が、もこきー向けにもシェアできるようにフォークしてくれていたんですね。
そこで他鯖へのシェアできるようにする改造ありがとう他のみんなもこんな感じでやってね的な意味で、「他鯖への対応大歓迎です」って引用したら、(確実にこれは言葉足らずな俺が悪いんだけれども6、)io以外のサーバにもシェアできるように実装していただいたんです!
しかもちゃんとシェア先のサーバーをストレージに保存するという。最高ですね。
(実はこの話まだたこさんにしてない。その節はなんかこっちからお願いしたような形になっちゃってすいませんでした、そして嫌な顔せず速攻PRだしてくれてありがとうございます)
当時のスクショ
v0.2.0系統
その後、5月頃ですかね、Twitterシェアボタンが存在しないページでも共有できるように、画面右下に拡張機能付属のシェアボタンを設置しました。これは今ではTwishareの象徴(だと勝手に思ってる)デザインです7。ここがv0.2.0ですね。
まあこれも簡単な構造で、現在のページのタイトルとURLを取得して/shareに投げてるだけです。
それから、ボタンの表示非表示に関する設定とか諸々を追加したりはしましたがまあ特筆することは、ないかなぁ。
あえていえば、現在表示中のページがMisskeyまたはFirefish(旧Calckey)であるかの判別方法は書いておきましょうか。
これは、MisskeyのページをMisskeyにシェアしたとて仕方ないのでMisskey上では非表示にするオプションに使用してます。
meta
タグのname
属性を元に判断してますね。
async function isMisskey() {
// MisskeyやCalckeyでは、metaタグのname='application-name'にcontent='Misskey'とか'Calckey'がついてる
let metatags = document.getElementsByTagName('meta');
const misskeys = ['Misskey','Calckey','Firefish'];
for (const metatag of metatags){
if (
metatag.getAttribute('name') === 'application-name' &&
misskeys.includes(metatag.getAttribute('content'))
){
return true;
}
}
return false;
}
それから、ポップアップのデザイン改善を行いました8。
その後、鈴音りんさん(@rin_jugatla@misskey.io )にプルリクエストをいただき、YouTubeとかで全画面表示にしてる時に右下のシェアボタンが隠れるようにしていただきました。
僕基本的に全画面表示しない人なので、こういうの気づかないんで、めちゃありがたかったです。
それから、Twitterにおけるツイート詳細画面をシェアする際に、ツイート内容をMFM9の引用型としてノートに貼り付ける機能を実装しました。v0.2.5です。これは自分でもよくやったと思う。元々Misskeyには、ツイートのURLを貼るとツイートを埋め込んでくれる機能はあるんですけど、これをすることでツイートを展開しなくても内容はわかるようになった。
この際、ツイート内でTwitterユーザー名に対してメンションが行われていた時にMisskey側でメンションされないように、<plain>
で囲む工夫を行いました。(無駄に1行で書いてるね。なんでだろうね。分けた方が読みやすいのにね10。)
const replacedTweetText = tweetText.split('\n').map(line => line ? '><plain>' + line + '</plain>' : '>').join('\n');
(中略)
let shareText = `${replacedTweetText}\n>by <plain>${tweetUsername}</plain>\n\n${urlToShare.href}`;
ほぼ同時に、ツイート内に絵文字が含まれていると、(Twemojiは画像扱いされるので)Misskey側には表示されなかったため、TwemojiをUnicode絵文字に置換するように変更をも行いました。
単にTwemojiのimage
タグのalt
属性と置き換えてるだけです。
const tweet = document.querySelector('article div[data-testid="tweetText"]');
//TwemojiのUnicode絵文字化
const twemojis = tweet.querySelectorAll('img');
for (const twemoji of twemojis) {
const emoji = twemoji.alt;
const emojiTextNode = document.createTextNode(emoji);
tweet.replaceChild(emojiTextNode, twemoji);
}
const tweetText = tweet.textContent;
この辺でv0.2.6出してますね。
v0.3.x系統とv0.4.x系統
右下のシェアボタンが動かせるようにしてほしいという要望があったので、ドラッグもしくはスワイプで移動できるようにしました。
要素のドラッグは、ググればいくつか実装例は出てくるんですけど、だいたいそういうのってそのままじゃ上手くいかないんですよね11。
Twishareの場合、ドラッグするのがボタンであること、スクロールしても画面の定位置で留まっているべきということなど(他にも問題あった気がするけど忘れた)があり、その上僕の技術力もそこまである訳ではないので、悪戦苦闘しながら実装していました。主に英語の授業中に12。(おい
それで完成したのがこんなコードです。(長いから畳んどきますね。)
ボタンをドラッグさせるコード
//以下クリックorドラッグ時
//変数の定義
let isDragging = false;
let isClickEnabled = true;
let cursorX;
let cursorY;
//挙動を設定:マウスドラッグ
//ブラウザーデフォルトのドラッグを無効化しておく
button.ondragstart = () => {
return false;
};
button.addEventListener('mousedown',(event) => {
if (event.button === 0) {isDragging = true};
});
button.addEventListener('mousemove',(event) => {
if (isDragging && event.button === 0) {
//ドラッグ
isClickEnabled = false;
cursorX = event.clientX;
cursorY = event.clientY;
//なんか配置がうまく行かなかったので要素幅の3/4倍がちょうどよかった
button.style.top = (cursorY-3*button.getBoundingClientRect().height/4) + 'px';
button.style.left = (cursorX-3*button.getBoundingClientRect().width/4) + 'px';
} else {
return;
};
});
button.addEventListener('mouseup',(event) => {
if (event.button === 0) {
isDragging = false;
if (isClickEnabled) buttonClicked();
isClickEnabled = true;
};
});
//予期せずボタン外でマウスアップされてもドラッグをオフにする
window.addEventListener('mouseup',() => {
isDragging = false;
isClickEnabled = true;
});
//挙動を設定:スワイプ
button.addEventListener('touchstart',(event) => {
//画面スクロールを止める
event.preventDefault();
isDragging = true;
});
button.addEventListener('touchmove',(event) => {
if (isDragging) {
event.preventDefault();
//スワイプ
isClickEnabled = false;
cursorX = event.changedTouches[0].clientX;
cursorY = event.changedTouches[0].clientY;
//なんか配置がうまく行かなかったので要素幅の3/4倍がちょうどよかった
button.style.top = (cursorY-3*button.getBoundingClientRect().height/4) + 'px';
button.style.left = (cursorX-3*button.getBoundingClientRect().width/4) + 'px';
} else {
return;
};
});
button.addEventListener('touchend',(event) => {
event.preventDefault();
isDragging = false;
if (isClickEnabled) buttonClicked();
isClickEnabled = true;
});
//予期せずボタン外でタッチエンドされてもドラッグをオフにする
window.addEventListener('touchend',() => {
isDragging = false;
isClickEnabled = true;
});
右下にあるシェアボタンが邪魔で非表示にしている場合でもシェアできるように、拡張機能のポップアップ(サーバーとか設定するやつ)からもシェアできるようにするボタンの設置も行いました。
ただ、これはなんかFirefoxではうまく動かなかった13ので、Chrome向けリリースにしか含まれていません。(これがv0.4.0)
では、Firefox向けにはどうしたかと言うと、前述のボタンドラッグの機能だけを追加した、v0.3.0をリリースしました。
あと、Twishare本体とは別ですが、Twishareの右下のシェアボタンとほぼ同等の動きをするブックマークレットも作成しました。ぜひ使ってね。
ここまでが現在の開発状況です。
なお、冒頭にも書きましたが、受験生なので、今後は少なくとも年度末までは更新できないんじゃないかなぁと思ってます。
今後の展望
(現在のユーザー数)
↑ありがたいことにこの拡張はおよそ600弱ものユーザーに使用していただいていますので、暫くは更新を続けていきたいと思っています。みんな使ってくれてありがとね14。
まずは、Firefoxをv0.4.x系統(ポップアップからもシェアできるようにするやつ)にあげたいかなぁと。
ほかには、ちょくちょく右下のシェアボタンが崩れるので、原因特定と修正をしたいですね。ユーザーの皆さんももし見つけたら気軽に教えてください。
最後に、他の僕のMisskey関連の作品も紹介しておきます。
他の人のノートを自分のプロフィールにピン留めできるプラグイン
https://gist.github.com/alpaca-honke/8b9763e89f5642596ec022926f573e5d
未来情報産業株式会社さんのMisskey Shareを使用してMisskeyにシェアするためのURLを簡単に作れるサイト
以上、ぱーーって書いたので異常なほど読みづらかったかと思いますけど読んでいただきありがとうございました!
(いつもはもっと時間かけて書くけど、今回は時間なかったんでごめんなさい)
-
時間ないってのもあるけど基本面倒くさがりなので ↩
-
いや確認しろよ ↩
-
多くのインスタンスがあるソフトウェアなので、開発チームが各サーバーとは独立で管理しているMisskeyの公式サイト ↩
-
リアクション絵文字の1種。 ↩
-
大量のリアクションやリノート(リツイート的な)によって通知が荒れること ↩
-
マジでこいつ日本語大丈夫か? ↩
-
ポップアップの設定画面の方が象徴的とする説もある。開発者もようわかっとらん ↩
-
初期のデザインが壊滅的すぎた ↩
-
Misskeyの各所で使用できる、文字列装飾用の書式。もともとはMarkdownの派生だったらしいが、MFM関数の充実で色んな装飾ができるようになっている。参考: Misskey Hub ↩
-
こいつアホかな。 ↩
-
コピペで済まそうとすな ↩
-
去年のアドベントカレンダーでも、英語の授業中に書いたとか言ってた気がするこいつ。真面目に授業受けろよ ↩
-
FirefoxはChromeよりもセキュリティを重視している傾向があるので、特に拡張機能周りの機能は制限されてることが多い ↩
-
ほんとに感謝 ↩