もっとメッセージを

電子メールが大流行だが、部内コミュニケーション程度ならハイパーカードで十分。これまで扱ってきた仕組みを整理し、実用的なメッセージシステムを作成する。

メッセージを効率的に流す

これまでの連載で、メッセージをスタック起動時に表示する方法(1995/7号:掲示板データベースの作成)と即時配信する方法(1995/10号:電子速達メール)を、異なる切り口から紹介してきた。情報の内容によって、すぐに知らせるべきことや、翌朝仕事前に読んでもらいたいものなど違いがあるだろうから、これらを使い分けるのは一見合理的に見える。しかし、即時メッセージを配信したときに出張していた人はどうなるだろう? あるいは2人が続けてメッセージを出してしまったら、先に出したメッセージは読んでもらえるのだろうか?

今回はこれまでのまとめも兼ねて、両者を統合したもっと柔軟なメッセージシステムを検討してみる。さらに、メッセージ読み出しだけでなく、作成(発信)もシステマティックに行うための機能を追加してみよう。

まず、これまで作成したメッセージ配信の仕組みを整理してみると:

Aスタック起動時メッセージの場合

  1. 起動時にメッセージリストをチェック
  2. トップに来るタイトルがまだ読んだことのないものであれば
  3. そのタイトルを既読として記録し、最新のメッセージを表示する

B即時配信(速達)メッセージの場合

  1. idleを利用してメッセージファイルの更新時刻をチェック
  2. ファイルが新しくなっていたら
  3. その更新時刻を記録し、メッセージの内容を表示する

という流れであった。仕組みとして一長一短はあるが、配信機能の実現方法という点では、どちらも本質的には同じこと。これらを統合するのはそう難しくはないはずだ。では、そのためには何が必要で、その結果どんなメリットが得られるだろうか。

問題点の整理

要件を整理しよう。1)発信側の立場でいえば、メッセージによって速報性のあるものとそうでないものを使い分けたい。2)一方で受信側からみれば、メッセージの読み漏らしが生じないような仕組みが不可欠。3)さらに、メッセージは蓄積され、あとで閲覧できる方が便利である。これらの要件を満たしたシステムが今回の課題だ。

問題点は、最初に述べたように、ユーザーがいつ情報を受け取ってくれるかが不確実だというところである。速達のつもりでメッセージを出しても、不在ならば翌日のメッセージと同じことだ。また、最新のメッセージ1件のみを読みとるようにしていると、2)の読み漏らしの問題は避けられない。

これらをうまく処理するために、次のような方針でシステム設計に臨むことにする。

方針1:メッセージはHDB(*注1)のテキストデータベースとして蓄積し、このファイルが新しくなっていたら、新規メッセージを表示する

方針2:即時配信、翌朝配信は、メッセージをHDBに変換するタイミングで使い分ける

方針3:ユーザーごとに、どこまでが既読メッセージであるかを確認できる情報を持つ

以下、それぞれの方針について、目的の確認と処理方法の検討を行なっていく。

方針1:テキストデータベースとしてのメッセージ

これは、メッセージをHDBのテキストデータベースに蓄積していくことで、過去の情報を参照したり加工したりして再利用できるようにしようというものだ。こうすると、メッセージが複数ある場合でも、データベースからレコードを複数拾ってくることで、読み漏らしをなくすという課題も解決できる(画面1)。

メッセージをHDBとする方法は7月号で検討した。新しいメッセージのたびに、それをHDBファイルの先頭のレコードに加えていく。リスト1は、7月号のインデックス作成スクリプトの改訂版である。1行ずつデータを検証し、区切り行が現れたら次の行はタイトル、その次の行からは内容とみなして処理し、区切り行の位置をインデックスとして記録するというものだ(*注2)。

この原理は簡単だが、これらの手続きを毎回手作業で行うのではメッセージを送る意欲が失せてしまう。発信をスムーズに行うために、メッセージ作成と同時にデータベースを自動生成する仕組み(メッセージ作成カード)をあとで考えることにしよう。

受信側のスタックでは、起動時とidle時にこのデータベースファイルをチェックし、ファイルが新しくなっていたらメッセージが配信されたものとみなして表示する手続きをとる。

方針2:メッセージをHDB変換のタイミングで使い分ける

起動処理の流れ図 「データベースファイルが更新されたときにメッセージ配信とみなす」ということは、逆に言えば、「メッセージ本文を作成しても、データベースに組み込まないうちは配信されない」ということを意味している。これを利用し、即時配信ならメッセージをすぐにデータベースとして登録(HDB変換*注3)する一方、翌日配信分は、配信日の朝一番にまとめてHDB変換するということにすれば、ユーザーへの配信タイミングを調節できる。

即時配信のデータベース作成は前節で述べたとおりの作業だが、翌朝まとめてHDB変換するには工夫が必要だ。メッセージを作成・保存する機能と、データベース化の機能を分離しなければならない。

いろいろ考えた結果、筆者は次のような方法を採用した。

  1. メッセージを“表示すべき日付をファイル名”としたテキストファイルに蓄積しておき
  2. 朝、最初に起動されたコンピュータが“その日の日付のファイル”があるかどうかを確認して
  3. ファイルを見つけたらその時点でHDB変換を行う

というやり方だ。早起き鳥のコンピュータがデータベースを更新すると、あとから来るメンバーはその更新されたファイルを確認してメッセージを受け取ることになる(図1)。

リスト2が実際のスクリプトである。今日の日付からファイル名を決定し(3行目)、目的のファイルがあれば、まずその内容をメッセージファイルの先頭につけ加え(5〜6行目)、改めてデータベースを更新する(7行目)。この作業は1度で十分なので、今日のファイルは削除しておく(8行目)(*注4)。ここまでは、最初に起動されたコンピュータのみが行う作業だが、残りは全てのコンピュータに関係する。朝一番でファイルを更新したのだから、当然、11行目ではファイルが新しいという答えが返ってくる。その結果、全員が12〜13行目でメッセージボードを見ることになるというわけだ。

方針3:どこまでが既読メッセージであるかを確認する

メッセージが頻繁に送られるようになると、ユーザーの読み出しタイミングまでに複数のメッセージが蓄積されるということが生じてくる。これを漏れなく全部読むためには、誰がどこまで読み終えたのかを確認できなければならない。そのために、8月号で考えたようなユーザーID(*注5)を使い、各人が最後に読んだメッセージのタイトル(以下、既読タイトルとする)をファイルに記録しておくことにしよう。例えばIDが10番のユーザーなら、記録ファイルの10行目にその既読タイトルが保存されるという具合だ。

リスト3に、今回のサンプルスタックで新規メッセージを表示するためのスクリプトを示した(*注6)。既読メッセージの確認機能は5行目以降である。WhatYouReadは、各メンバーの既読タイトルが記録されているファイルだ。6〜7行目でユーザーの既読タイトルを調べ、9〜14行目でこれを全タイトルのリストと順番に比較していく。一致するタイトルが見つかったら、表示すべきメッセージはそのひとつ前までということになる(11行目)。あとはインデックスから表示すべき行番号を取得し(18行目)、それに従って必要なメッセージを表示する。

15〜16行目は、既読タイトルの記録部分。そして17行目はメッセージファイルの更新時刻の保存である。これらの手続きで、次に伝えるべきメッセージをシステムが判断できるように記録しているわけだ。

メッセージ作成カードをつくる

メッセージを伝えるルールが複雑になってきたので、簡単にメッセージを作成する機能も用意しておきたい。画面2のようなカードを作って、メッセージを記入してボタンを押すだけで、適切なタイミングでメンバーに配信できるようにしよう。

配信タイミングに応じてメッセージを処理し分けるスクリプトはリスト4のようになる。3〜5行目ではフィールドのテキストに、発信日、発信者名、HDB形式の区切り行などを加えたメッセージの原型を作る。6行目でラジオボタンのハイライトを調べ、速達かどうかを判断して分岐処理を行う。

速達の場合はすぐにHDBを更新する。7行目でファイルを読み込んで新しいメッセージを先頭に加え、改めて保存した上でインデックスを更新するのだ。メンバーはidleハンドラによる受信機能で、直ちにお知らせを受け取ることになる。

翌日配信の場合は、配信日をファイル名とすることになっていたから、まずその日付を取得しなければならない。16〜23行目の関数messageDate()は「引数の翌日」を返すものだ(土日休みの場合を考慮して20行目で土曜の日付を月曜日に修正している)。この関数の結果を受けてファイルを特定し、その先頭にメッセージを追加して保存しておく(12〜13行目)。あとはリスト2によって、翌朝いちばんに起動されたコンピュータがメッセージファイルに追加してくれるという手はずだ。

個人宛メールへの応用

最後に、全メンバーを対象にした掲示板のようなシステムに加え、特定のメンバーに私信を送る方法も簡単に検討しておこう。ここまでお付き合い下さった読者ならもうお察しの通り、ファイルの名前をユーザーIDとし、メンバーは自分のID番号に当たるファイルの有無を調べるようにすれば、すぐに個人宛メールへの応用ができる。

リスト4の11行目で翌日の日付をファイル名として取得しているが、ここで

ask "メールを送る相手のIDを入力して下さい"

などとしておけば(*注7)、目的のIDファイルを作成できる。idleハンドラでは、自分のIDとなっているファイルをチェックし、新しいファイルが送られていたらこれを表示する。

if there is a file pMsg & myID() then...

のような形だ。ユーザーごとにHDBデータベースを作っても良いが、そこまでする必要もないのなら、読み終わったファイルを消去しておけば読み漏らしやメッセージの重複を避けることができる。

今、盛んに大企業が電子メールを導入し始めているが、全社ベースのシステムは一朝一夕には整わない。それまでは、部内のコミュニケーション程度ならハイパーカードのメッセージシステムで十分。アプリケーションソフトに無闇に投資をせず、その分ペーパー資産の電子化などにお金をかけた方が得策だと思うがどうだろうか。

*注1

7月号で作成したHyperDB形式のデータのこと。レコードをハイフン2つの行(--)で区切り、各レコードの最初の1行を見出しとするテキストファイル。添付CD-ROMに、これまでの連載のテキストをこの形式で収録している。

*注2

HDBの詳しい説明は7月号の記事を参照されたい。今回のスクリプトは、よりネットワークを意識し、インデックスとタイトルをフィールドではなくファイルに保存している。

*注3

データベース登録、HDB変換、更新などといろいろな言い方をしているが、具体的には前節で述べたように、メッセージをHDB形式のファイルに追加し、インデックスを更新することを意味している。

*注4

ファイルの削除はハイパートークではできないのでXFCNが必要(サンプルスタックに用意)

*注5

8月号では個々のユーザを特定するために、userNameというグローバル変数を「03 Kan」のようにID番号とニックネームの組み合わせにする方法を考えた。

*注6

速達、翌日いずれの場合も、メッセージの表示はメッセージカードに置いたこのスクリプトを使う。サンプルスタックではもう少し細かい処理も行っているが、説明用に簡略化した。

*注7

メンバーリストを使ってポップアップメニューを用意する方法もある。8月号も参照。

リスト

リスト1

-- SaveFileはテキストをファイルに保存するハンドラ
-- 使い方はSaveFIle FileName, Text
-- サンプルではスタックスクリプトにある

 1: on updateMsgData msgStr
 2:   global pMsg -- メッセージファイルのあるフォルダ
 3:   put "0" & RETURN into Indexes -- 0行目を仮想的に区切り行扱いする
 4:   put "--" into separator
 5:   put TRUE into header -- タイトルかどうかを判定するフラグ
 6:   repeat with i=1 to number of lines of msgStr
 7:     if header is TRUE then
 8:       put line i of msgStr & RETURN after titles
 9:       put FALSE into header
10:     else if line i of msgStr is separator then
11:       put i & RETURN after Indexes
12:       put TRUE into header
13:     end if
14:   end repeat
15:   SaveFile pMsg & "Titles", titles
16:   SaveFile pMsg & "Indexes", Indexes
17: end updateMsgData

リスト2

-- ReadFile()はテキストをファイルから読み出す関数
-- サンプルではスタックスクリプトにある

 1: on morningMsg
 2:   global pMsg, fMsg -- fMsgはメッセージデータベース名
 3:   put pMsg & the date into TodayMsgFile -- 日付がファイル名
 4:   if there is a file TodayMsgFile then
 5:     get ReadFile(TodayMsgFile) & ReadFile(pMsg & fMsg)
 6:     SaveFile pMsg & fMsg, it
 7:     updateMsgData it -- リスト1
 8:     get DeleteFile(TodayMsgFile) -- XFCN
 9:   end if
10:   get item 4 of FileInfo(pMsg & fMsg) -- XFCN
11:   if it > cd fld "LastMsg" of cd "MsgBoard" then
12:     go cd "MsgBoard"
13:     showNewMsg -- リスト3
14:   end if
15: end morningMsg

リスト3

-- myID()はユーザーのID番号を取得する関数
-- サンプルではスタックスクリプトにある
-- Index,Titleはファイルに保存される(リスト1参照)ので、
-- まずそれを取り込むことで最新のデータベースを利用する(3〜4行目)

 1: on showNewMsg
 2:   global pMsg, fMsg
 3:   put ReadFile(pMsg & "Titles") into fld "Title"
 4:   put ReadFile(pMsg & "Indexes") into fld "Index"
 5:   put pMsg & "WhatYouRead" into RecFile
 6:   put ReadFile(RecFile) into recentRec
 7:   put line myID() of recentRec into lastTitle
 8:   get fld "Title"
 9:   repeat with i=1 to number of lines of it
10:     if line i of it is lastTitle then
11:       put i-1 into recNo -- i番目以降は既読
12:       exit repeat
13:     end if
14:   end repeat
15:   put line 1 of it into line myID() of recentRec
16:   saveFile RecFile, recentRec
17:   put item 4 of FileInfo(pMsg & fMsg) into cd fld "LastMsg"
18:   put line RecNo+1 of fld "Index" into pos
19:   get ReadFile(line 2 of fld "FileInfo")
20:   put line 1 to (pos - 1) of it into fld "TEXT"
21:   show fld "TEXT"
22: end showNewMsg

リスト4

 1: on mouseUp
 2:   global pMsg, fMsg, userName
 3:   put cd fld 1 into str -- メッセージを記述するフィールド
 4:   put the date & ": " before str 
 5:   put RETURN & userName & RETURN & "--" & RETURN after str
 6:   if hilite of btn "速達メッセージ" is TRUE then
 7:     put ReadFile(pMsg & fMsg) after str
 8:     SaveFile pMsg & fMsg, str
 9:     updateMsgData str -- リスト1
10:   else
11:     get messageDate(the date)
12:     put ReadFile(pMsg & it) after str
13:     SaveFile pMsg & it, str
14:   end if
15: end mouseUp

16: function messageDate when
17:   convert when to dateItems
18:   add 1 to item 3 of when -- 翌日の日付
19:   convert when to dateItems
20:   if item 7 of it is 7 then add 2 to item 3 of when-- 翌日が土曜の場合
21:   convert when to short date
22:   return when
23: end messageDate

用語

ハイライト

ボタンの属性の一つ。通常はFALSE、黒く反転していればTRUEとなる。ラジオボタン、チェックボックスの場合は、選択/チェックされている状態がTRUE。チェックボックスでは、「オートハイライト」をTRUEにしておけば、クリックするたびにTRUE/FALSEが入れ替わる。一方ラジオボタンの切り替えは、オートハイライトをFALSEにしてスクリプトで制御しなければならない。

ハイパーカード2.2以降は「ファミリー」という属性が追加され、ファミリー番号を同じに設定しておくと、そのグループで常に1つのボタンだけがハイライトされるようになり、ラジオボタンの制作が簡単になった。

ポップアップメニュー

これもハイパーカード2.2でボタンに加わった機能の一つ。ボタンの形式を「ポップアップ」に設定し、「内容...」にメニューで表示する中身を設定してやればよい。2.1以前ではHPopUpMenuなどの外部命令を使って実現する。

(MacUser Japan, November 1995)