ひまつぶし雑記帖

おひとり様ActivityPubサーバー自作実装メモ

2023/9/8 [15:45:34] (金) 天気

ActivityPubを通じてMastodonなどFediverseと呼ばれるネット連合からアカウントとして認識されるように最低限のサーバー構成を作ったのが7/12。その後、秘密鍵・公開鍵を使えるperlのモジュールをlolipopレンタルサーバーにインストールできたことで、それっぽいものにしあがって、実際に運用して今日に至る。

「ため池」
↑perlで自作実装したおひとり様ActivityPubサーバー

そもそもMastodonやMisskeyなどのサーバーを立てるスキルがない。わたしには難易度が高すぎる。
自分のわかる範囲で自作実装したので、間違ってる・勘違いしてるところもあるかも知れないけど、今のところ意図通り。
この雑記帖と同じレンタルサーバーに展開して追加の費用なども必要ない。

自作とはいえ、スグ忘れるのでまとめてメモしておこう、というのがこのエントリ。
5分以上前の自分はアカの他人。

Fediverseからアカウントとして認識されるために必要なもの
1)nodeinfo(json)
 サーバー情報。
2)host-meta(xml)
 webfingerのURL。
3)webfinger(json)
 ユーザー情報のURL。
4)actor.json(json)
 ユーザー情報。type:Personのjson。
上から順番にアクセスされて4のユーザー情報にたどり着けばActivityPubサーバーのアカウントとして認識される。
1と2はなくても大丈夫らしいけど、webfingerのURLは「/」だったり「/.well-known/」だったりするので、2の「/host-meta」で教えてあげる。

「ActivityPubを使ってFediverseにたどり着く」
↑各ファイルの詳細。


おひとり様サーバーとして動作しているスクリプトは2つだけ。
・飛んでくるリクエストをさばく門番的スクリプト
・タイムラインを表示、投稿、管理するスクリプト


【飛んでくるリクエストをさばく門番スクリプト】

ActivityPubでの会話を求めて飛んでくるリクエストはいろいろ。
アカウント情報を取得するためにactor.jsonをリクエストされるし、ActivityとしてCreate(投稿)Follow(フォロー)などのリクエストが飛んでくる。

まず「.htaccess」を設置
rewrite ruleでパラメータをつけて門番スクリプトにリダイレクトするところからスタート。

リダイレクトされてリクエストを受けつける門番スクリプトの仕事は以下。

サーバー情報やユーザー情報はGETでリクエストが飛んでくる。
リクエストされたURLに応じて、門番は用意してある静的ファイル、jsonやxmlファイルを返す。
ActivityPubのjsonファイルは「Content-Type: application/activity+json; charset=utf-8」をヘッダにつけるのが必須。

followerやfollowingにアクセスされた時はfollower、followingを記載したテキストファイルを読んで、静的ファイルにそのリスト情報をくっつけて返すようにした。

Activityリクエストは「ユーザー名/inbox」にPOSTでリクエストが飛んでくる。
これはまとめて門番で受け取って。
・HTTPヘッダ類
・content(ボディ)
これをひとつのログファイルとして、所定のディレクトリに保存する。
ボディ=jsonを読んで、CreateだのFollowだのUndoだのログファイル名の一部にしておく。
門番スクリプトの仕事はここまで。

ヘッダ類もログファイルに収録するのは。
公開鍵とhttp signatureで、飛んできたリクエストが正しく署名されているか検証する必要がある。
Mastodonなんかはたぶんリクエストが来たらその場で検証までしてるはずで、ログファイルなど必要ないと思う。
わたしの場合、まだよくわかってないことが多くて、ログファイルで確認したいことがあるので、そのためにログファイルを残している。

ActivityのPOSTリクエストのうちいくつかは今時点スルーしている。
仕様的にはMUSTとかSHOULDだったりするものもあって、対応が必要と思われるけど、ウチの運用的にそれ必要?と微妙なもの…要検討。


【タイムラインを表示、投稿、管理するスクリプト】
あまり考えず「ため池」という名前にした。

Mastodonでいうところの「ローカルタイムライン」のために投稿用のデータベースを用意。

といっても、テーブル2つだけ。それも1つはログインに使うだけのもの。投稿用のテーブルはテキストと、画像のURLや幅・高さぐらいを記録するメモ帳。
投稿するには重複しない一意のIDが必要で、それにはデータベースが便利に使える…ほぼそのため。

おひとり様サーバー、ユーザーはわたしだけ。サーバーに登録しているユーザーの投稿を表示する「ローカルタイムライン」は、わたしの壁打ちとなる。


フォローしたひとの投稿を表示する「ホームタイムライン」はファイル管理。

自分以外、ひと様のデータを持たないのが原則。ひと様のデータを溜め込むといろいろ考えなきゃいけないことが出てくるから。

ため池にアクセスすると、門番スクリプトが保存したログファイルを開いて。
まずはHTTP SignatureのVerify。
actor情報をリクエストして、公開鍵を取得。公開鍵を使ってログファイルのヘッダ情報にあるSignatureを検証する。
Signatureに問題がなければ、Activityに応じた所定のフォルダに、所定の命名規則で名前をつけたjsonファイルを保存。元のログファイルを削除。

「RSAモジュールで公開鍵と秘密鍵」
「HTTP Signatureをlolipopレンタルサーバーで作成」
↑秘密鍵、公開鍵について

タイムライン用に保存するjsonファイルはCreateかAnnounceのNote
・上限20個
・期間1日
ディレクトリのファイルを読んで制限以上になっていたら削除。
フォローイングが増えてくると、読まないまま削除される方が多いし、さらにフォローは増やすつもりなので、読む・目にする投稿の方が圧倒的に少なくなる。知らない間に流れてきて知らないうちに削除されている。

でも、SNSはそのぐらいで適正だと思う。
その時その場で見たものがすべて。「袖すり合うも他生の縁」だ。

「ホームタイムライン」用に保存するjsonファイルは「to」か「cc」に「Publish」(全公開)が指定されているもの限定(サークル投稿を除く)
この「ホームタイムライン」を表示するにはログインが前提。つまり、見ることができるのはわたしだけだ。

ホームタイムラインの投稿にフォローしたひとのアイコンや名前を表示させるために、actor.jsonをリクエストしてpersonを取得してそこからアイコンのURLや名前を取ることになる。
これだけのためにリクエストが生じるんで、ホームタイムラインの表示が重い。

[10/24 09:27:53]
さすがに重すぎでキレた…ので、不本意ながら、表示名とアイコンURLに限っては、キャッシュすることにした。
当然、表示は劇的に早くなった。
キャッシュの期間は30日(要検討)。
Updateのリクエストがpersonだったら確認してキャッシュに反映。

本当だったら非同期で情報を更新したいところだけど、Javascriptとか面倒くさいし、メンテやエラーの特定にあっちもこっちもになってコスト高なので却下。手動更新。

見るのが自分ひとり、一般公開はしないということから、表示が重いのは我慢、また、同じ理由でいくつかのリクエストはスルーしてもいいかな、と。

[10/04 12:53:50]
というのが、ログファイルが貯まるとさすがに重すぎるので、このフェーズだけ抜き出したスクリプトを作ってcronで5分間隔で回すようにした。


MentionとFollowは別扱い。
両方とも確認してから返事するなりフォロー返しするなりしてから手動で削除することにした。

FollowをAcceptしたら、Followのjsonを保存して、followersのリストに登録する。
Following、こちらからフォローする場合も同じ。Followingに使ったjsonを保存して、followingのリストに登録する。Followingのjsonを保存するのは、フォロー解除するUndoのために、元になったjsonが必要だから。uuidでIDを作ることにしてしまって、残しておかないと元のjsonを再現できないというオチ。

Mention
連絡先としてサイトや名刺など今まではtwitterのアカウントを入れたんだけど、twitterがなくなったんで、こっちを「連絡先はこちら」にしたい。Mentionを見落としちゃまずいんで、これも確認からの手動にした。

[09/21 10:05:12]
リクエストとして飛んでくるブーストは対応済みで、こちらからブーストのリクエストを飛ばすのも実装してみた。
「おひとり様ActivityPubサーバーにブースト実装」
いろいろ考えなきゃいけないことあった(改めて)

[10/03 10:57:44]
「おひとり様ActivityPubサーバーのjsonの実例」
投稿するのに実際に使ってるActivityPubのJSONの実例をメモした。

[10/06 10:26:14]
「おひとり様ActivityPubサーバーにLike実装」
Likeされる方じゃなくて、する方の実装。
WEB拍手みたい感じで使うのがいいかな、と。

[12/22 07:40:43]
「ActivityPubサーバーのGETリクエストに署名」
GETリクエストでactor情報などを取得する時にHTTP Signatureが必要なサーバーもあった。
GETにも署名することにした。

[12/27 10:01:38]
「おひとり様ActivityPubサーバーにBlock実装」
レアケースだけどBlockも対応した。

[2024/01/23 09:08:36]
「おひとり様サーバーの改修」
Fedibird.comやkmy.blueなど一部のサーバーが実装しているサークル機能に対応…といっても読むだけ。サークル作成やサークル投稿に対する返信は未対応。

■以上、雑なまとめでした■
ディテールというか無駄の多いエントリはカテゴリからどうぞ。
「ActivitiyPub::ひまつぶし雑記帖」


以下のサイトを参考にさせてもらっておひとり様ActivityPubサーバーを実装することができた。感謝深謝。
「NetlifyとSupabaseでほぼ静的なActivityPubサーバ」
「Fediverse入門―非中央集権型SNSサーバを作ろう!」
「田舎の昼のサイレンbotをActivityPubで実装する(マストドンにアカウントを認識してもらう編)」
「ActivityPubの実装についてのメモ」

ActivitiPub本家本元。
「ActivityPub」
「ActivityPubの仕様(和訳)」

image
[12/13 08:30:17]追記。
Activityなどの実装・対応状況

[済]Create
noteを投げ込むのに使用
[済]Follow
followしたらfollowingリストに追加。json保存。
followされたらfollowersリストに追加
[済]Accept
followされたら許可
[未]Reject
followされたら拒絶
[済]Update
noteやpersonなどオブジェクトの更新…だけど、personだけ対応
[済]Delete
自分がポストしたNoteの削除。
ポストされて流れてきたNoteの削除(20個しか保存しないので、すでに削除済みのことが多いけど念のため)
[未]Add
オブジェクトの追加
[未]Remove
オブジェクトの削除
[済]Block
ブロックされたらリストに登録。間違えてFollowするといけないので、Followリクエストを飛ばす時にブロックリストも確認する。
[済]Like
いいね。
したらjson保存。14日間。
されたら1日だけ表示。jsonは3日間だけ保存。
[済]Announce
ブースト。
したら、7日間ため池データベースに保存。期限がすぎたら削除、undo
されたものは削除
ブーストされたらLikeと同じ対応にした
[済]Undo
やり直し。
ブーストの取り消しとフォロー解除は対応済み。Likeは未対応。


[済]Mention
DMみたいなものだからJSON保存。確認して手動で対応。
[済]Hashtag
お一人さまだと意味はないけどとりあえず
[未]Replies
リプライは実装しない方向で

Json-LDの署名について不明。手がかりにできそうなURL
jsonの日本語訳PP版
MastodonのJSON-LD署名と検証
MastodonのJSON-LD署名の実装rubyのコード
JSON-LD関連のコード
graph rdfとかよくわからんけど、関係しそうなところ
LDのAPI

»電子書籍制作代行についてはこちら

chromebookでftp

2021/9/8 [08:06:59] (水) 天気

chromebookで使い勝手の良いFTPクライアントが見当たらない。
一応いくつかサイトを作って公開していて、HTMLやCSS、perlのソースを編集更新してたりするんで、FTPは必須。
とはいえ、あれこれ放置気味で、いまの状況についていけてないところもあって、メンテしなきゃあと思いつつ時間も作れず…だったんだけど、chromebookでお手軽unix環境も手に入ったことだし、もろもろ調べてみようと。

linux環境のlftpが一番手軽で簡単っぽいのでメモ。
リモート(サーバー側)からディレクトリ構造ごとローカルにもってくるのは
lftpでログインしてlftpのプロンプトで

mirror -v --delete --only-newer PATH

とやれば一網打尽

chromebookはlinux環境ですべてが揃う、というオチ、というか小ネタ。

image

»電子書籍制作代行についてはこちら

CotEditorからjavascript(JXA)経由でperlへ

2015/9/8 [08:45:50] (火) 天気

CotoEditorで昨日書いたapplescriptと同じことをするjavascript(JXA)を書いたのでメモ。

YosemiteからはOS標準でJavaScript for Automation (JXA)が使えるようになった。
歴史的な流れとしては、HyperCardのHyperTalkからapplescriptへ、applescriptからjavascriptへということらしい。これらは乱暴に理解すると、unixでのシェルスクリプトやWINDOWSのバッチファイルみたいなもの、か。いや、macのことはよく知らない。

こちら、 『JavaScript for Automation (JXA)』鳶嶋工房 がとても参考になった。

これからはjavascriptだというし、初めてでよくわからなかったapplescriptよりは多少いじったこともあるjavascriptの方がたぶん早いだろうと、昨日applescirptで書いたばかりだけど、javascriptで書き直した。

ここんとこいじってるスクリプト、マクロはxyzzyもCotEditorのスクリプトも身も蓋もない言いかたをすると「シェルに渡すためのコマンドライン文字列を生成するだけ」のもの。

実際の作業はperlのスクリプトが担当なのでマクロやスクリプトでは難しいことは何もしていない。

javascirpt(JXA)版スクリプト

CotEditorのスクリプトとして使うには
・ファイル名(拡張子)「.js」
・ファイルの一行め「#!/usr/bin/osascript -l JavaScript」
この2点が必要。



HOMEディレクトリ(のpath)を取得するのに、わざわざstdlib使うのって大袈裟な感じやな、とか思ったりしつつ、アプリをいじるための呪文
var app = Application("CotEditor");
app.includeStandardAdditions = true;
を入れてプロパティを調べるぐらいで、後はほんとJavascript。
わたしの慣れの問題で当たり前の話、applescriptよりJavascirptの方が格段にわかりやすい。わからないところ・書き方などはJXAというかJavascirptで検索すれば情報豊富で解決も早い。

今回、macの「スクリプトエディタ.app」が手軽・便利で大助かりだった。変数に何が入ってるのかを見られたり、エラーチェックできて、こんなアプリがあったとはちょっとした発見&驚き(今さら感)

ただ、残念なことに、このJavascript版はosascriptで使うと
warning: failed to get scripting definition from /usr/bin/osascript; it may not be scriptable.
という警告が毎回出てしまう。いちいちコンソールが出て警告されるのは鬱陶しいけどしかたがないらしい。
こちらが参考になった→ 『JXAの「Error: A privilege violation occurred」の回避』

せっかくだけど、昨日のapplescriptはお蔵入り・塩漬けだなあ。

image 

»電子書籍制作代行についてはこちら

iTunes Storeから初の売り上げ報告など

2012/9/8 [15:40:00] (土) 天気

8月15日に公開となった「小説同人誌Select」の初の売り上げ報告が、昨日9月7日にきた。無料DLの数は(「売り上げ」にはならないので)入っていない。無料ものに関しては日々のDL数をsales and trendsのレポートから自分でチェックする必要があるのか。隠してもしょうがないんでぶっちゃけると。8月の売り上げは4本。250円x0.7 x 4 ということで700円でありました。そのうち3本は誰が買ったか知ってるというお友達購入。しょぼっ!…って、最初はこんなものだろう。
そもそも作品数が足りない・ぜんぜん露出が足りないので、先日も書いたように、作品を地道に増やしていって、アドワーズも含めて広告・告知戦略を考える。創作文芸のジャンルは確かに売るのが難しいマニアックな世界なんだけど、面白い小説があるのでそれを伝えることさえできれば、イケんじゃないかと。
「創作文芸見本誌会場 HappyReading」なんて場所を提供している立ち位置にいて、実際に読んでこれが面白かった、と具体的に挙げるのはまずいと思うんで、タイトル・作者を言えないのがもどかしい、かなあ。この夏の収穫(コミケ、コミティア)でスゲーのがあるんだけど(語彙貧困)

ちなみに、先週8月31日(金)に作品追加してWaiting For Review の待機状態だったのが、今朝 In Reviewになっていた。問題がなければ、明日か週明けあたりに公開、か。前回、前々回と Waiting For Review から In Review になるのに2週間かかったのが、今回は1週間。早くなるのはありがたい。けど、期間が読めないと、作業の段取りとか立てにくいなあ。
てことで、なんとか頑張ろう。

あれ?何円以上で振込まれるのか・振込手数料はどのぐらい取られるのか、よくわかってないぞ、おれ。


そういや、twitterが2013年3月で仕様変更とか。
https://blog.jp.twitter.com/2012/08/twitter-api-v11aip.html
・APIすべてで認証が必要となる。
・APIの使用回数制限が1時間60回となる。
というのが大きいのかな。ウチはユーザー認証に使ってるだけなので関係ないなあ、と思ってたら。
・RSS ATOMが廃止となる。
というのが引っかかった。 「創作文芸見本誌会場HappyReading」 の詳細ページで、登録したひとのtwitterの名前(screen_name)を表示してリンクしているんだけど、安直にrssを読みにいって名前を取得していた。これが使えなくなる。認証が必要なAPIで、users/lookup.json というAPIが一回100人分のデータを取得できるので、こっちに変更。でも、アクセスのたびに、APIにリクエストしてたんじゃ、1時間60回なんてスグ。twitterの名前を取得するためだけに、リクエスト回数を消費するのももったいないので、週一回cronで走らせてDBに登録することにした。
今回の仕様変更はtwitterの自由な広がりを奪うことになる。のは確かで、クライアント、アプリ開発者が非難囂々なのはよくわかる。でも、もしかしたら、ユーザーにとっては統一された使い勝手となっていいことかもしれない。macがiOSレベルでtwitterをサポートするのも何か影響があったのかな。

小説同人誌Select  公開中
・iTunes Store はこちら→ https://itunes.apple.com/jp/app/id546230414?mt=8


»電子書籍制作代行についてはこちら

週刊ダイヤモンド

2009/9/8 [23:20:31] (火) 天気

今週号は必見。
「新宗教」特集で、神社本庁、創価学会、幸福の科学、天理教など日本の宗教団体の洗い出し。新潮あたりだと思想的な部分も触れるんだけど、ダイヤモンドって経済誌なもんだから組織や資金の側面だけ。その分遠慮がない、かも。ひたすら好奇心を満たしてくれる690円だった。今のうちに買っておかないと、信者が買占めに走る・圧力がかかって店頭からなくなる、てなことまで考えられる内容、というと言い過ぎか。
久しぶりに好奇心満足。

»電子書籍制作代行についてはこちら

VQ1005で少しだけ路地を徘徊

2008/9/8 [21:21:11] (月) 天気

代々木上原は坂道と猫の多い街だ。せっかくの好天、うろつき徘徊したかったけど、例によってなんだかてんてこまい。データ登録したらしただけ、いろいろ不備も出てくる見つける。なもんで、放り込んだら終わり、じゃなくて、放り込んで作ったら始まり、なんだよなあ。ていうか、むしろそっちの方が時間や労力のかかる力仕事

image 
VQ1005

すっかりVQ1005がお気に入りだぜ。
ちなみに、路地の奥にみえるのは首のない鉄鎧じゃなくて、壊れた井戸とそこに立てかけられた壊れた樋です。

»電子書籍制作代行についてはこちら

profile

profile

 
doncha.net
contact:
»運営者
@t2aki@tokoroten.doncha.net

ため池

[2024/10/09 23:24]
ActivityPub Implementations
https://syobon.notion.site/a5e9e010575f499fb9f1520436ac2d75
ActivityPubサーバーもいろいろあるんだ。てか、こんなにいろいろあるのはいいなあ。

以前も投稿したEpicyonのスクショはなかなか衝撃的なので再掲。
No timeline algorithms.
No javascript.
No database. Data stored as o ...

[2024/10/09 17:58]
「ちっこい」「オモチャみたいな」
ノパソがほんと欲しいんだけど、需要がないのかなあ。大艦巨砲主義的なゲーミングPCは、わたしは必要ない。

[2024/10/09 17:51]
縦軸:弱い↓↑強い
横軸:馬鹿←→賢い

…ううう、どうやら左下っぽいなあ。せめて左上になりたい人生だった。

@t2aki@tokoroten.doncha.net

検索
<<2024/09>>
       
1234567
891011121314
15161718192021
22232425262728
2930

リンク

WINDOWS版サウンドノベル
おかえりください PC WINDOWS版サウンドノベル
『おかえりください』体験版

[2 Page] »
1 2

TOTAL:2991

2024 (14)
1 (2)
2 (2)
3 (1)
5 (1)
7 (2)
8 (2)
9 (3)
10 (1)
2023 (53)
1 (1)
2 (5)
3 (1)
4 (1)
5 (3)
6 (9)
7 (9)
8 (6)
9 (5)
10 (3)
11 (2)
12 (8)
2022 (16)
1 (1)
3 (2)
6 (2)
7 (1)
8 (4)
9 (2)
10 (1)
11 (2)
12 (1)
2021 (12)
1 (3)
2 (1)
6 (1)
8 (2)
9 (1)
10 (1)
11 (2)
12 (1)
2020 (18)
1 (2)
2 (6)
4 (1)
6 (1)
7 (2)
8 (2)
12 (4)
2019 (17)
1 (3)
2 (4)
3 (2)
4 (2)
5 (1)
6 (1)
8 (1)
10 (1)
12 (2)
2018 (21)
1 (3)
2 (2)
3 (2)
4 (1)
5 (1)
6 (6)
8 (1)
9 (1)
10 (2)
12 (2)
2017 (32)
1 (2)
2 (1)
4 (2)
5 (1)
6 (6)
7 (3)
8 (5)
9 (3)
10 (2)
11 (2)
12 (5)
2016 (41)
1 (5)
2 (5)
3 (2)
4 (3)
5 (4)
6 (6)
7 (2)
8 (2)
9 (3)
10 (1)
11 (4)
12 (4)
2015 (99)
1 (11)
2 (12)
3 (9)
4 (6)
5 (8)
6 (8)
7 (3)
8 (5)
9 (16)
10 (6)
11 (1)
12 (14)
2014 (112)
1 (16)
2 (5)
3 (6)
4 (12)
5 (16)
6 (19)
7 (9)
8 (6)
9 (4)
10 (8)
11 (6)
12 (5)
2013 (145)
1 (24)
2 (15)
3 (18)
4 (23)
5 (14)
6 (11)
7 (7)
8 (11)
9 (5)
10 (4)
11 (6)
12 (7)
2012 (103)
1 (1)
2 (1)
3 (4)
4 (3)
5 (7)
6 (26)
7 (17)
8 (5)
9 (8)
10 (10)
11 (11)
12 (10)
2011 (54)
1 (4)
3 (7)
4 (4)
5 (14)
6 (6)
7 (3)
8 (3)
9 (1)
10 (4)
11 (2)
12 (6)
2010 (70)
1 (12)
2 (7)
3 (6)
4 (6)
5 (3)
6 (10)
7 (6)
8 (4)
9 (3)
10 (4)
11 (3)
12 (6)
2009 (144)
1 (15)
2 (12)
3 (12)
4 (6)
5 (15)
6 (6)
7 (10)
8 (9)
9 (17)
10 (12)
11 (14)
12 (16)
2008 (148)
1 (10)
2 (6)
3 (10)
4 (11)
5 (13)
6 (10)
7 (13)
8 (19)
9 (18)
10 (12)
11 (13)
12 (13)
2007 (106)
1 (7)
2 (5)
3 (3)
4 (7)
5 (5)
6 (9)
7 (8)
8 (13)
9 (18)
10 (11)
11 (8)
12 (12)
2006 (158)
1 (28)
2 (28)
3 (25)
4 (7)
5 (9)
6 (7)
7 (12)
8 (13)
9 (10)
10 (7)
11 (6)
12 (6)
2005 (350)
1 (31)
2 (26)
3 (26)
4 (27)
5 (29)
6 (30)
7 (32)
8 (30)
9 (30)
10 (32)
11 (29)
12 (28)
2004 (292)
1 (24)
2 (24)
3 (29)
4 (27)
5 (28)
6 (25)
7 (26)
8 (24)
9 (12)
10 (19)
11 (26)
12 (28)
2003 (318)
1 (22)
2 (25)
3 (21)
4 (28)
5 (28)
6 (28)
7 (28)
8 (29)
9 (26)
10 (29)
11 (28)
12 (26)
2002 (317)
1 (29)
2 (26)
3 (26)
4 (25)
5 (28)
6 (30)
7 (27)
8 (21)
9 (25)
10 (27)
11 (28)
12 (25)
2001 (277)
1 (17)
2 (21)
3 (23)
4 (20)
5 (31)
6 (18)
7 (26)
8 (25)
9 (29)
10 (19)
11 (24)
12 (24)
2000 (53)
6 (9)
7 (4)
8 (2)
9 (3)
10 (1)
11 (15)
12 (19)
1999 (3)
7 (1)
10 (2)
1998 (18)
9 (9)
10 (7)
11 (2)