taQu_rule(5) taQu Rule Engine Reference taQu_rule(5)

Name

taQu_rule — taQu カスタムルール記述言語リファレンス (v1.12.0)

Synopsis

メタデータ宣言
    rule = "ルール名"  |  rule += "追記"
    description = "説明文"  |  description += "追記"
    maxAns = 数値
    const NAME = 整数
    変数.label = "ラベル"
    変数.color = "CSS color" / correctColor / wrongColor
    変数.size = normal|small
    mark.symbol = "マーク"
    sync(変数)
    keep(変数)
    winText = "文字列"
    showWinRank = true|false
    sortByWinRank = true|false
    sortByPressOrder = true|false

イベント定義
    def correct():  |  def wrong():  |  def through():
    def push():    |  def next():    |  def judge():

制御構文
    if 条件:  |  elif 条件:  |  else:
    switch 式:  |  case 値:  |  default:
    repeat N:

コマンド
    win()  |  lose()  |  lock()  |  unlock()
    tLock()  |  tUnlock()  |  pass
    giveAns(条件)  |  throughAns()
    othersAdd(var, val)  |  othersSet(var, val)
    tAdd(var, val)  |  tSet(var, val)
    setBorderColor(色)  |  resetBorderColor()
    broadcast(var, 式)  |  resetVar(var)
    setFlavorText(文字列)  |  resetFlavorText()
    setX/Y/Z/W(式)  |  addX/Y/Z/W(式)
    scope(条件):

Description

taQu のカスタムルールを記述するためのインデントベース言語です。Python ライクな構文でクイズ早押しゲームのルールを定義します。

関連資料: tanakata-ro/taQu_algorithm

文字コード
UTF-8 推奨。
コメント
# 以降の文字はすべて無視されます。
インデント
半角スペースによるインデントでブロックを表現します。
文字列リテラル
"..." および '...' のどちらも使用できます。

Metadata

def の外側(ファイル先頭付近)に記述します。

構文説明
rule = "名前" ルール名。[定数名] で定数値を展開できます。+ による文字列連結、数値式の文字列化、+= による追記も使えます。name は rule の別名です。
description = "説明" ルールの説明文。同様に [定数名] 展開、+ 連結、数値式の文字列化、+= 追記が使えます。desc は description の別名です。
maxAns = N 1 問あたりの最大回答人数。デフォルト 1。0 で無制限(エンドレスチャンス)。
const NAME = 整数 定数を定義します。実行時に必ず定数値で上書きされます。ルール選択 GUI から変更可能。
x|y|z|w.label = "文字列" 変数のラベルを設定します。未設定の変数は画面に表示されません。
x|y|z|w.label = "" ラベル名を表示せず、数値だけを表示します。例: x.label = ""
x|y|z|w.color = "CSS color" / correctColor / wrongColor 数値表示の文字色を設定します。例: x.color = "#ef4444", y.color = "red", z.color = "rgb(59,130,246)", w.color = correctColor。correctColor / wrongColor は正解・誤答色を参照し、色反転設定に追従します。空文字 "" を指定すると通常色に戻ります。
x|y|z|w.color = "CSS color" / correctColor / wrongColor inside def / if イベント処理中にも数値表示色を変更できます。例: if x % 2 == 0: x.color = correctColor。変更後は全員の画面に反映されます。
x|y|z|w.size = normal|small 数値表示のサイズを設定します。normal(通常)または small(小さめ)。"小さめ" も指定できます。
mark.symbol = "マーク" mark の表示マークを設定します。未設定時は ×。5個まではマークを並べ、6以上は マーク+数字 で表示します。miss は旧名として引き続き使えます。
sync(x|y|z|w) 変数をチーム内で同期します(詳細は TEAM セクション参照)。
keep(x|y|z|w|mark) ルール適用時やラウンド切替時に、指定した変数の現在値を保持します。GUI の「保持する変数」チェックでも設定できます。
winText = "文字列" WIN バッジに表示するテキストを変更します。デフォルトは WIN。トップレベルの if / elif / else と def 内で使用できます。変更後に勝利したプレイヤーから適用され、すでに勝利表示されたプレイヤーの文言は変わりません。
showWinRank = true|false WIN バッジに 1st / 2nd などの順位を表示するかどうかを設定します。デフォルトは true。
sortByWinRank = true|false ボタン押下時、押したプレイヤーを winRank(勝ち抜け順位)の昇順で並べます。winRank が同じ場合は押下時刻で比較します。デフォルトは false。
sortByPressOrder = true|false ボタンを押したプレイヤーをリスト先頭に移動し、押下時刻順に並べます。false にすると押下しても並び順が変わりません。デフォルトは true。
const TARGET = 7
rule = "セブンス[TARGET]"
description = "[TARGET]ポイント先取で勝利"
maxAns = 1
x.label = "pts"
x.color = "#ef4444"
y.label = ""
y.color = "rgb(59,130,246)"
w.label = "bonus"
w.size = small

rule と description は、トップレベルの if の中でも変更できます。

const TARGET = 10
rule = "Freeze" + TARGET
description = "勝ち抜け: " + TARGET + "問"

if TARGET >= 10:
    rule += " hard"
    desc += " / 高難度"

Variables

組み込み変数(表示用)

数値欄として画面に表示できる変数は x, y, z, w の 4 つです。ラベル未設定の変数は非表示になります。4 つすべてを表示する場合は、プレイヤーカードが 1 変数分だけ横に広がります。miss は数値欄ではなく、カード上の × 表示専用変数です。

組み込み読み取り専用変数

変数名説明
mt チームインデックス。チーム名をアルファベット順にソートした順番(1 始まり)。チーム未所属のソロは 0。
miss プレイヤーカードに × を表示する専用変数です。miss += 1 で×が1つ増えます。値は自動的に 0 以上 5 以下に丸められ、負の値にはなりません。Type Aではフレーバーテキスト横、Type Bでは名前と得点欄の間の専用スペースに表示されます。

早押し遅延変数

buttonDelay は各プレイヤーの早押し遅延をミリ秒単位で表します。参照と代入が可能で、値は 0~60000 の整数に丸められます。変更は実際の早押しボタンと設定画面に同期されます。

buttonDelay = 200

def correct():
  buttonDelay += 50

def next():
  scope(x >= 3):
    buttonDelay = 500
  scope(x == 0):
    my_buttonDelay = 0

カスタム変数

x, y, z, miss 以外の任意の名前(例: hp, phase)も使用できます。def の外で代入すると初期値として扱われます。画面には表示されません。カスタム変数は各プレイヤーごとに独立して保持され、scope() 内での代入も正しく各プレイヤーに保存されます。

phase = 0   # 全プレイヤーの初期値を 0 に設定

def push():
  if phase == 0:
    phase = 1
    scope(my_mt == mt):
      phase = 1   # チームメンバーにも phase=1 が保存される

my_ プレフィックス

scope() の条件式・ブロック内、および othersAdd / othersSet / tAdd / tSet の値式の中で使います。

参照(読み取り)
コマンドを発行したプレイヤー自身の変数を参照します。
代入(書き込み)
scope() のブロック内で my_x = 式 と書くと、各対象プレイヤーへの処理中に発行者自身の変数へ書き込みます。+= / -= / *= も使用可能です。
othersSet(x, my_x)       # 自分以外全員のxに「自分のx値」をセット
scope(mt == my_mt):       # 自分と同じチームのプレイヤーに実行
    x += my_x             # 対象メンバーのxに自分のxを加算(参照)

scope(mt == my_mt and x >= 10):
    my_z += 1             # 条件を満たすチームメンバーの数を発行者のzに積算(代入)

チーム集計関数

構文説明
tSum(var) チーム全員(自分含む)の指定変数の 合計 を返します。
tMax(var) チーム全員(自分含む)の指定変数の 最大値 を返します。
tMin(var) チーム全員(自分含む)の指定変数の 最小値 を返します。
tCount(var) チーム内で指定変数が 0 でないメンバーの数 を返します。引数に 1 を指定するとチームの総人数を返します。
tProd(var) チーム全員(自分含む)の指定変数の 積 を返します。

全体集計関数

構文説明
rankVal(var, n, 昇順=false) 全プレイヤー中で指定変数 var の値が n 番目のプレイヤーのその変数値を返します。未指定時は降順です。3つ目の引数に true または 1 を指定すると昇順になります。n が範囲外の場合は 0 を返します。
rank(並び順...) 現在のプレイヤーの順位を返します。/win と同じ並び順指定が使えます。例: rank(x -y) は x 降順、同点なら y 昇順で順位を返します。
countIf(条件式) 全プレイヤーのうち、条件を満たすプレイヤーの人数を返します。条件には x, y, z およびカスタム変数を使用できます(各プレイヤーの値で評価されます)。countIf(true) や countIf(1) は全員を数えます。

数学関数

構文説明
floor(式) 小数切り捨て。
ceil(式) 小数切り上げ。
round(式) 四捨五入。
sqrt(式) 平方根。
pow(式a, 式b) 式a の 式b 乗。
abs(式) 絶対値。
sign(式) 符号を返します。正なら 1、負なら -1、0 なら 0。
max(式a, 式b) 2 つの値の大きい方を返します。
min(式a, 式b) 2 つの値の小さい方を返します。
clamp(式, lo, hi) lo 以上 hi 以下に値を制限します。
not(式) 論理否定。式が 0 なら 1、それ以外なら 0。
rand(min, max) min 以上 max 未満のランダムな整数を返します。対象プレイヤーごとに独立した乱数が生成されます。
random(n) 0 以上 n 未満のランダムな整数を返します。
/(除算)演算子はすでに整数除算(切り捨て)として動作します。floor() / ceil() は複合式の切り捨て・切り上げに使います。

Events

def イベント名(): の形式でイベントハンドラを定義します。def の外側に書いたコードは、プレイヤーが参加した時点の初期化処理として実行されます。

Custom Functions

def name(): で引数なしの自作関数を定義し、name() で呼び出せます。 イベント名や組み込み関数名など、予約済みの名前は自作関数として定義できません。

def addCorrectPoint():
  x += 1
  if x >= 3:
    win()

def correct():
  addCorrectPoint()
イベント名実行タイミング対象
def correct(): ホストが「正解」を押したとき 現在の回答者のみ
def wrong(): ホストが「不正解」を押したとき 現在の回答者のみ
def through(): ホストが「スルー」を押したとき 回答者がいれば回答者のみ。いなければ誰にも実行されない
def push(): プレイヤーが早押しボタンを押した瞬間 押したプレイヤーのみ
def next(): maxAns = 1(またはデフォルト)のとき: correct / wrong / through のいずれかの後
maxAns ≠ 1(エンドレス・複数正解など)のとき: correct / through のみ(wrong では発火しない)
全プレイヤー(一人ずつ順番に実行)
def judge(): ホストが /judge を実行したとき 順位が確定していないプレイヤー

push() 内の処理順序

push() でキューに積んだコマンドは、ルール実行後に以下の順で処理されます。

  1. scope() — 対象プレイヤーへの変数操作
  2. giveAns() — 解答権の付与
  3. throughAns() — 解答権の放棄(カーソル前進)
  4. 解答者の決定(updateRoomAnswerState)
giveAns() が先に実行されてから throughAns() でカーソルが調整されるため、「他の人に渡して自分は解答権なし」という組み合わせが正しく動作します。

Operators

代入演算子

演算子意味
x = 式代入
x += 式加算代入
x -= 式減算代入
x *= 式乗算代入

算術演算子

演算子意味備考
+加算
-減算
*乗算
/整数除算(切り捨て)ゼロ除算は 0 を返す
%剰余ゼロ除算は 0 を返す
^べき乗x ^ 2 → x の 2 乗

比較・論理演算子

演算子別記法意味
==等しい
!=等しくない
>より大きい
<より小さい
>=以上
<=以下
and&&論理積(両方真のとき 1)
or||論理和(どちらか真のとき 1)

条件分岐 (if / elif / else)

# 基本形
if x >= 10:
    win()
else:
    x += 1

# elif を使った多段分岐
if x >= 10:
    win()
elif x >= 5:
    z += 1
elif x >= 1:
    z += 0
else:
    lose()

# ネスト
if x > 0:
    if x == 5 and y == 0:
        z += 2
    else:
        z += 1

# 1行 if(インライン形式)
if x >= 10: win()
elif は else if の省略形です。連続して何個でも書けます。switch/case と異なり、前のブロック内で変数を変更すると後続の条件に影響します。

繰り返し (repeat)

repeat 3:
    x += 1   # x を 3 回加算 → x += 3 と等価

# 式も使用可能
repeat N:
    othersAdd(y, 1)
repeat の回数は整数に切り捨てられます。1000 回を超える場合は安全のため 1000 回に制限されます。

多分岐 (switch / case / default)

変数や式の値に応じて処理を切り替えます。マッチしたケースだけが実行され、以降のケースには処理が流れません(フォールスルーなし)。連続する if と異なり、あるケース内で変数を変更しても他のケースに影響しません。

switch phase:
    case 0:
        x += 3
        phase = 1
    case 1:
        x += 1
        phase = 0
    default:
        pass
複数の if を並べると、1つ目の if 内で変数を書き換えた場合に2つ目の if が意図せず真になることがあります。フェーズ遷移のような「状態で処理を切り替える」用途では switch/case を推奨します。

Commands

状態変更コマンド

コマンド説明
win()勝利状態にします。以降の処理は実行されません。
lose()敗北状態にします。以降の処理は実行されません。
lock()そのプレイヤーの回答権をロック(ボタンを押せなくする)。
unlock()ロックを解除します。
tLock()チーム全体の回答権をロックします。
tUnlock()チーム全体のロックを解除します。
pass何もしません(空のブロックで使用)。
setBorderColor(色)そのプレイヤーのカード囲み線の色を設定します。色は #rrggbb、red など CSS に有効な値を指定します。
resetBorderColor()setBorderColor() で設定した周り線の色をリセットします。
setX(式) / setY(式) / setZ(式) / setW(式)x = 式 などのショートカット。他の変数名と区別したい場合に便利です。
addX(式) / addY(式) / addZ(式) / addW(式)x += 式 などのショートカット。
broadcast(var, 式)全プレイヤーの変数 var を 式 の値に一括セットします。othersSet と異なり発行者自身にも適用されます。
resetVar(var)カスタム変数 var を初期化ブロックで設定した初期値に戻します。
setFlavorText("文字列")そのプレイヤーのフレーバーテキストを指定した文字列に変更します。チームへの帰属も変化します。最大 8 文字。
resetFlavorText()setFlavorText() で変更したフレーバーテキストを空文字列にリセットします。

解答権操作コマンド

コマンド使用可能なイベント説明
giveAns(条件) すべてのイベント 条件を満たすプレイヤーの中からランダムに 1 人を選び、解答権を付与します。
push() 内:まだボタンを押していない(isPressed=false)プレイヤーが対象になります。
correct / wrong / through 内:その問のリセット後に実行されるため、ボタンを押していたプレイヤーも対象になります。
発行者自身と、ホストは対象外です。
throughAns() 主に push() ボタンを押した事実(isPressed)は残したまま、そのプレイヤーの解答権だけを消します。同じターン内で再度ボタンを押すことはできません。
giveAns() と組み合わせることで「ボタンを押したら自分ではなくチームメンバーが回答者になる」仕組みを実現できます。
# push() でチームメンバーに解答権を渡し、自分は解答しない例
def push():
    if phase == 0:
        giveAns(my_mt == mt)   # 同チームのメンバーに解答権を渡す
        throughAns()            # 自分の解答権を放棄(isPressed は残る)
        scope(my_mt == mt):
            phase = 1
throughAns() は push() 以外(correct / wrong / through)でも呼べますが、それらのイベントでは通常のリセット処理と組み合わせるため動作が複雑になります。push() での使用を推奨します。

他プレイヤーへの操作コマンド

コマンド対象説明
othersAdd(var, val) 自分以外の全員 var に val を加算します。
othersSet(var, val) 自分以外の全員 var を val にセットします。
uAdd(var, val) 自分以外の全員 othersAdd の別名。
uSet(var, val) 自分以外の全員 othersSet の別名。
tAdd(var, val) 同チームのメンバー(自分を含む) var に val を加算します。
tSet(var, val) 同チームのメンバー(自分を含む) var を val にセットします。

得点への対他コマンドは記述順に反映され、その後の行は反映後の値を参照します。初期化用のトップレベルでは使用できません。 scope の内側からさらに対他コマンドや解答権操作を呼び出す場合も、読み込み時にエラーになります。 得点更新は保存済みの不在プレイヤーにも適用されますが、司会・観戦者は対象外です。

Team

プレイヤーは参加時の「フレーバーテキスト」でチーム分けされます。

フレーバーテキスト動作
チーム名(コロンなし) 通常のチーム。同じ文字列のプレイヤーが 1 チームになります。
メタ名:サブ名(コロンあり) メタチーム構造。同じ「メタ名」を持つプレイヤーが大グループとして扱われます。

変数の同期設定 (sync)

チーム内で変数を全メンバーに共有したい場合に def の外側に記述します。

sync(x)   # x はチーム内で同期(誰かが更新すると全員に反映)
          # y, z は同期されず個人の値として保持

Scope

scope(条件): は発行者を基準として、発行者以外のプレイヤーに対して条件付きで処理を実行します。司会・観戦者は対象外です。

構文説明
scope(条件): 条件が真のプレイヤーに対してブロック内を実行します。
my_xxx scope 内で発行者の変数を参照します。
mt scope 内で各プレイヤー自身のチームインデックスを参照します。
イベント発行者(scope の基準)
push()ボタンを押したプレイヤー
correct() / wrong() / through()現在の回答者(ホストが判定したプレイヤー)
next()処理中の各プレイヤー自身
judge()処理中の各プレイヤー自身
def push():
    # 発行者と同じチームのプレイヤーにだけ実行
    scope(mt == my_mt):
        x += 1

    # 全員に実行(条件を常に真にする)
    scope(1):
        y += my_x
scope 内でも win(), lose(), lock() 等が使えます。ただし othersAdd / othersSet や tAdd などの対他コマンドは scope の外に記述してください。scope 内でのカスタム変数への代入は各プレイヤーに正しく保存されます。

Chat Commands

チャット欄にスラッシュで始まるコマンドを入力することで、ホストは各種操作を行えます。 コマンドはチャットには流れず、ホストのみに結果が返ります。

得点操作

コマンド説明
/set [x|y|z|w|mark] [値]全員の指定変数を値に設定します。mark は0以上の整数になります。
/add [x|y|z|w|mark] [値]全員の指定変数に値を加算します。mark は0未満にはなりません。
/hide [-c] x y z w指定変数をプレイヤーには - として表示します。-c を付けると完全に非表示にします。
/show x y z w非表示にした複数の変数を再表示します。
/judgeルール内の def judge(): を、順位が確定していないプレイヤーへ実行します。
/win [人数] 判定...指定した判定順で勝ち抜けを確定します。人数を省略した場合は、判定1位の同率者のみ対象になります。
/lose [人数] 判定...指定した判定順で失格を確定します。例: /win 3 x -y は x が大きい順、同点なら y が小さい順で判定します。

ジングル

外部ストレージの音声 URL を登録し、参加者全員のブラウザで同時再生します。 サーバーは URL 文字列のみを中継し、音声バイナリは一切保持・転送しません。

コマンド説明
/jingle load <名前> <URL>ジングルを登録します。同じ名前で上書き可。最大 20 個。
/jingle play <名前>登録済みジングルを全参加者に再生します。
/jingle stop全参加者のジングルを停止します。
/jingle list登録済みジングルの一覧をチャットに表示します(ホストのみ表示)。
/jingle remove <名前>指定したジングルを削除します。
/jingle load bgm1 https://dl.dropboxusercontent.com/s/xxxx/music.mp3
/jingle play bgm1
/jingle stop
/jingle list
/jingle remove bgm1

対応 URL 形式

サービス形式・変換動作
Dropbox 共有リンク(www.dropbox.com/...)をそのまま貼る。dl=1 不要。 ◎ 推奨
Google Drive /file/d/{id}/view 形式を自動変換。ただし CORS 制限により再生できない場合あり。 △ 環境依存
直接 URL 音声ファイル(mp3, ogg, wav 等)への直リンク。CORS 設定が必要。 ○
ジングルの登録データは揮発性です。部屋が削除されると消去されます。サーバー再起動時も失われます。
著作権を侵害する音源の使用は禁止です。ジングルとして使用する音源の著作権処理はご自身の責任で行ってください。本サービスは URL の中継のみを行います。

Webhook

ゲームイベント(ボタン押下・正誤判定・勝ち抜け・失格)が発生したとき、 指定した URL に JSON で POST 通知を送ります。 スコア集計・外部連携・自動記録などに使えます。

コマンド説明
/webhook <URL>Webhook を設定します。HTTPS の URL のみ有効。設定時に署名シークレットが自動生成されます。
/webhook offWebhook を解除します。シークレットと配信記録も同時に破棄されます。
/webhook status現在設定中の URL と、直近の配信結果(時刻・イベント・成否・所要時間)を確認します。
/webhook secret現在の署名シークレットを確認します。
/webhook https://script.google.com/macros/s/xxxx/exec
/webhook status
/webhook secret
/webhook off

送信されるイベント一覧

event説明data の主なフィールド
buttonPressed解答ボタンが押されたuserId, name
judged正誤判定が下されたaction (correct / incorrect / through), userId, name
playerWonプレイヤーが勝ち抜けたuserId, name, rank
playerLostプレイヤーが失格になったuserId, name

POST ペイロード例

{
  "event": "judged",
  "roomId": 1,
  "roomName": "クイズ大会",
  "ts": 1710000000000,
  "data": {
    "action": "correct",
    "userId": "abc123",
    "name": "田中"
  }
}

送信の仕様

項目内容
タイムアウト5 秒。応答が返らない場合は打ち切られます。
リトライありません。失敗した通知は失われます。
成功の判定HTTP ステータス 200〜299 を成功、それ以外を失敗として記録します。レスポンス本文は参照しません。
進行への影響ありません。受信側が停止していてもゲームは通常どおり進行します。
到着順保証されません。順序が必要な場合は ts で並べ替えてください。

受信側は、まず即座に 2xx を返し、重い処理は非同期に行ってください。

配信結果の確認

送信のたびに結果が部屋に 1 件記録され、/webhook status で確認できます。

[webhook] 設定中: https://example.com/taqu
最終送信: 2026/08/24 21:03:11 JST / judged
結果: 成功 200 OK(132 ms)
[webhook] 設定中: https://example.com/taqu
最終送信: 2026/08/24 21:03:11 JST / buttonPressed
結果: 失敗 HTTP 500 Internal Server Error(214 ms)

まだ一度も送信していない場合は 最終送信: なし と表示されます。失敗時の「原因」は次のいずれかです。

表示意味
タイムアウト(5秒)5 秒以内に応答が返らなかった。
HTTP 500 Internal Server Error など応答は返ったが、ステータスが 200〜299 以外だった。
fetch failed などのエラーメッセージ接続できなかった(DNS 解決失敗、TLS エラー、接続拒否など)。
記録は最後に送信を開始した 1 件だけです。複数の送信の完了順が前後しても、古い結果で上書きされることはありません。
送信中に /webhook off や URL の再設定を行った場合、そのリクエストの結果は記録にも失敗通知にも反映されません。

失敗したときの通知

送信に失敗すると、その部屋のホストにだけチャット欄で通知されます。参加者には表示されません。

[webhook] 送信失敗: 2026/08/24 21:03:11 JST / judged
原因: タイムアウト(5秒)

リクエスト署名

Webhook の POST リクエストには X-Taqu-Signature ヘッダが付与されます。 受信側でこのヘッダを検証することで、taQu サーバーからの正規リクエストであることを確認できます。

アルゴリズム
HMAC-SHA256。リクエストボディ(JSON 文字列)をシークレットで署名した hex ダイジェストを sha256= プレフィックス付きで送ります。
シークレットの取得
/webhook <URL> 設定時にチャットに表示されます。/webhook secret で再確認できます。
X-Taqu-Signature: sha256=a3f2c1...(64文字の hex)

検証の実装例(Python):

import hmac, hashlib

SECRET = b"your_secret_here"

def is_valid(request):
    body = request.body          # raw bytes
    given = request.headers.get("X-Taqu-Signature", "")
    expected = "sha256=" + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, given)

検証の実装例(Node.js):

const crypto = require("crypto");
const SECRET = "your_secret_here";

function isValid(rawBody, signature) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", SECRET)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signature || "");
  if (a.length !== b.length) return false;   // 長さが違うと timingSafeEqual は例外を投げる
  return crypto.timingSafeEqual(a, b);
}
署名検証には必ずタイミング安全な比較関数(hmac.compare_digest / crypto.timingSafeEqual)を使用してください。通常の文字列比較ではタイミング攻撃に対して脆弱です。

Google スプレッドシートへの記録

Google Apps Script でウェブアプリを作成すると、無料でスプレッドシートに自動記録できます。

  1. スプレッドシートを開き、拡張機能 → Apps Script
  2. 以下のコードを貼り付け、デプロイ → 新しいデプロイ → 種類: ウェブアプリ
    アクセスできるユーザー: 全員 に設定
  3. 発行された URL を /webhook <URL> で登録
function doPost(e) {
  const sheet = SpreadsheetApp.getActiveSheet();
  const body  = JSON.parse(e.postData.contents);
  const row   = [
    new Date(body.ts),
    body.roomId + ': ' + body.roomName,
    body.event,
    (body.data.action || ''),
    (body.data.name   || ''),
    (body.data.rank   || '')
  ];
  sheet.appendRow(row);
  return ContentService.createTextOutput('ok');
}
Google Apps Script は任意のリクエストヘッダを読み取れないため、X-Taqu-Signature の検証は行えません。 署名検証が必要な場合は Node.js / Python などのサーバーを受信エンドポイントとして用意してください。
また、Apps Script は初回実行が遅く 5 秒のタイムアウトに掛かることがあります。/webhook status に タイムアウト(5秒) が記録される場合は doPost の処理を軽くしてください。
Webhook の設定・確認ができるのはホストのみです。失敗通知もホストにのみ表示されます。
設定は揮発性です。部屋の削除・リセット時のほか、サーバー再起動時(日本時間 午前 5 時)にも URL・シークレット・配信記録が消去されます。/webhook <URL> で設定し直した場合も配信記録はリセットされます。
内部ネットワーク・プライベート IP への送信は拒否されます(SSRF 対策)。URL の検査は設定時にのみ行われます。

Examples

Free — 正解で〇、誤答で×が増えるシンプルなルール

description = "正解で〇、誤答で×が増えるシンプルなルール。"
name="Free"
maxAns=1
x.label = "〇"
y.label = "×"

def correct():
  x += 1

def wrong():
  y += 1
  miss += 1

def through():
  pass

色変化とmiss表示

description = "正解数が偶数なら赤、奇数なら黒。誤答で×表示も増える。"
x.label = "〇"
y.label = "×"

def correct():
  x += 1
  if x % 2 == 0:
    x.color = "red"
  else:
    x.color = "black"

def wrong():
  y += 1
  miss += 1   # 0〜5の範囲で×表示が増える

def through():
  pass

M〇N× — 設定した回数で勝敗が決まるルール

rule="[M]〇[N]×"
description = "[M]回正解で勝ち抜け、[N]回誤答で失格"
maxAns=1
x.label = "〇"
y.label = "×"
const M = 7
const N = 3

def correct():
  x += 1

def wrong():
  y += 1

def through():
  pass

def next():
  if x >= M: win()
  if y >= N: lose()

Freeze N — n回目の誤答でnターン休み

rule="Freeze[N]"
description = "[N]回正解で勝ち抜け、n回目の誤答でn回休み"
maxAns=1
x.label = "〇"
y.label = "×"
z.label = "Lock"
const N = 10

def correct():
  x += 1

def wrong():
  y += 1
  z = y + 1
  lock()

def through():
  pass

def next():
  if z > 0:
    z -= 1
    if z == 0:
      unlock()

  if x >= N: win()

Attack Survival — 正解で相手を削り、誤答で自分が削られるサバイバル

rule="Attack Survival"
description = "持ち点[M]が無くなると失格、正解で他のプレイヤー-1pt、誤答で自身が-[N]pt"
const M = 20
const N = 1

x.label = "Pts"
x = M

def correct():
  othersAdd(x, -1)

def wrong():
  x -= N

def next():
  if x <= 0:
    x = 0
    lose()

10by10by10mini — チーム内の正解数の積が[N]以上で勝ち

description = "チーム内の正解数の積が[N]以上で勝ち。誤答でその人のPtsが1にリセット。2回誤答でロック。ただし誰かが2回誤答すると、他の全チームの2回目の誤答が1回に戻り、ロックも解除される。"
rule = "10by10by10mini"
x.label = "Pts"
y.label = "×"
z.label = "Prod"
const N = 200
x = 1
z = 1
sync(z)
def correct():
  x += 1
  z = tProd(x)
  if z >= N:
    win()

def wrong():
  x = 1
  y += 1
  z = tProd(x)
  if y >= 2:
    lock()
  scope(mt != my_mt):
    if y == 2:
      y = 1
      unlock()
フェーズ遷移のある wrong() / correct() では switch/case を使うことで、あるケース内で phase を変更しても別のケースが意図せず実行されることを防げます。

AI Code Generation

下のプロンプトをコピーし、[要望] を書き換えてから、このページ全体(Ctrl+A で全選択)と一緒に AI に送信してください。

Const Descriptions

A const value shown in the GUI can have help text. Write it as a trailing comment, as the comment immediately before the const, or as the comment immediately after it. These comments are only UI metadata and do not affect rule execution.

const N = 3 # Number of correct answers needed to win

# Number of wrong answers before losing
const W = 2

const P = 1
# P: Points added on a correct answer

See Also

taQu ルールテスター — /rule_tester