# WeeChat スクリプト作成ガイド

Sébastien Helleu
flashcode@flashtux.org
バージョン 4.7.1

このマニュアルは WeeChat チャットクライアントについて説明しており、WeeChat の一部です。

Latest version of this document can be found on this page ↗.

## 1. イントロダクション

WeeChat (Wee Enhanced Environment for Chat) はフリー、高速、軽量な多くのオペレーティングシステムで動くチャットクライアントです。

このマニュアル文書は以下のスクリプト言語を利用して WeeChat 用のスクリプトを製作する方法を解説しています:

- Python

- Perl

- Ruby

- Lua

- Tcl

- Guile (Scheme)

- JavaScript

- PHP

注記 | この文書に含まれるほぼすべての例は Python で書かれていますが API は他の言語でも共通です。

## 2. WeeChat のスクリプト

### 2.1. WeeChat のアーキテクチャ

WeeChat はシングルスレッドで、この点はスクリプトも同じです。

スクリプトのコードは以下の場合に実行されます:

- スクリプトを読み込んだ時: 通常これは register function 関数を呼び出すことを意味します。

- WeeChat がフックコールバックを呼び出した時 (フックを参照してください)。

スクリプトのコードが実行されると、WeeChat は一時停止し、実行の完了を待ってから再開します。このため、スクリプトは操作をブロックしては 絶対にいけません。ネットワークを呼び出す等の場合には、`hook_process` 等の専用 API 関数を使ってください。

重要 | WeeChat がクラッシュするため、スクリプトで fork したりスレッドを作ることは 禁止 されています。これが必要な場合には専用 API 関数を使ってください。 どうしてもバックグラウンド実行を避けられない場合には `hook_process` 関数を使ってください。バックグラウンドプロセスの実行の例および WeeChat プラグイン API リファレンス ↗内の `hook_process` 関数の文書を参照してください。

### 2.2. 言語仕様

#### Python

##### Module

WeeChat defines a `weechat` module which must be imported with `import weechat`.
A Python stub for WeeChat API is available in the repository: weechat.pyi ↗.

##### Functions

Functions are called with `weechat.xxx(arg1, arg2, ...)`.

Functions `print*` are called `prnt*` in python (because `print` was a reserved keyword in Python 2).

##### Strings received in callbacks

In Python 3 and with WeeChat ≥ 2.7, the strings received in callbacks have type `str` if the string has valid UTF-8 data (which is the most common case), or `bytes` if the string is not UTF-8 valid. So the callback should take care about this type if some invalid UTF-8 content can be received.

Some invalid UTF-8 data may be received in these cases, so the callback can receive a string of type `str` or `bytes` (this list is not exhaustive):

API function | Arguments | Examples | Description
--- | --- | --- | ---
`hook_modifier` | `irc_in_yyy` | `irc_in_privmsg irc_in_notice` | A message received in IRC plugin, before it is decoded to UTF-8 (used internally). It is recommended to use modifier `irc_in2_yyy` instead, the string received is always UTF-8 valid. See function `hook_modifier` in the WeeChat plugin API reference ↗.
`hook_signal` | `xxx,irc_out_yyy xxx,irc_outtags_yyy` | `*,irc_out_privmsg *,irc_out_notice *,irc_outtags_privmsg *,irc_outtags_notice` | A message sent by IRC plugin, after it is encoded to the `encode` charset defined by the user (if different from the default `UTF-8`). It is recommended to use signal `xxx,irc_out1_yyy` instead, the string received is always UTF-8 valid. See function `hook_signal` in the WeeChat plugin API reference ↗.
`hook_process hook_process_hashtable` | `-` | `-` | Output of the command, sent to the callback, can contain invalid UTF-8 data.

#### Perl

##### Functions

Functions are called with `weechat::xxx(arg1, arg2, ...);`.

#### Ruby

##### Initialization

You have to define weechat_init and call register inside.

##### Functions

Functions are called with `Weechat.xxx(arg1, arg2, ...)`.

Due to a limitation of Ruby (15 arguments max by function), the function `Weechat.config_new_option` receives the callbacks in an array of 6 strings (3 callbacks + 3 data strings), so a call to this function looks like:

    Weechat.config_new_option(config, section, "name", "string", "description of option", "", 0, 0,
                              "value", "value", 0, ["check_cb", "", "change_cb", "", "delete_cb", ""])

And the function `Weechat.bar_new` receives the colors in an array of 4 strings (color_fg, color_delim, color_bg, color_bg_inactive), so a call to this function looks like:

    Weechat.bar_new("name", "off", "0", "window", "", "left", "vertical", "vertical", "0", "0",
                    ["default", "default", "default", "default"], "0", "items")

#### Lua

##### Functions

Functions are called with `weechat.xxx(arg1, arg2, ...)`.

#### Tcl

##### Functions

Functions are called with `weechat::xxx arg1 arg2 ...`.

##### Null values

Since Tcl only has string types, there’s no null type to pass as an argument when a function accepts null values or to get as an argument in a callback function. To overcome this the WeeChat API defines the constant `$::weechat::WEECHAT_NULL` which acts as a null value. This constant is defined as `\uFFFF\uFFFF\uFFFFWEECHAT_NULL\uFFFF\uFFFF\uFFFF`, so it’s very unlikely to appear unintentionally.

You can pass this constant when a function accepts null as an argument and you will get it as the value of an argument in a callback function if the argument value is null. To see which functions accept null values and passes null values to callbacks, look at the Python prototypes in the WeeChat plugin API reference ↗.

#### Guile (Scheme)

##### Functions

Functions are called with `(weechat:xxx arg1 arg2 ...)`.

The following functions take one list of arguments (instead of many arguments for other functions), because number of arguments exceed number of allowed arguments in Guile:

- config_new_section

- config_new_option

- bar_new

#### JavaScript

##### Functions

Functions are called with `weechat.xxx(arg1, arg2, ...);`.

#### PHP

##### Functions

Functions are called with `weechat_xxx(arg1, arg2, ...);`.

### 2.3. 関数の登録

全ての WeeChat スクリプトは WeeChat に自分自身を「登録」し、登録はスクリプトの最初で行われなければいけません。

プロトタイプ (Python):

    def register(name: str, author: str, version: str, license: str, description: str, shutdown_function: str, charset: str) -> int: ...

引数:

- name: 文字列型、スクリプトの内部名

- author: 文字列型、作者名

- version: 文字列型、スクリプトのバージョン

- license: 文字列型、スクリプトのライセンス

- description: 文字列型、スクリプトの短い説明

- shutdown_function: 文字列型、スクリプトがアンロードされた際に呼び出される関数の名前 (空文字列でも可)

- charset: 文字列型、スクリプトの文字コード (UTF-8 はデフォルトの文字コードなので、スクリプトが UTF-8 で書かれている場合、空文字列を指定してください)

各言語で書かれたスクリプトの例:

- Python:

    import weechat

    weechat.register("test_python", "FlashCode", "1.0", "GPL3", "Test script", "", "")
    weechat.prnt("", "Hello, from python script!")

- Perl:

    weechat::register("test_perl", "FlashCode", "1.0", "GPL3", "Test script", "", "");
    weechat::print("", "Hello, from perl script!");

- Ruby:

    def weechat_init
      Weechat.register("test_ruby", "FlashCode", "1.0", "GPL3", "Test script", "", "")
      Weechat.print("", "Hello, from ruby script!")
      return Weechat::WEECHAT_RC_OK
    end

- Lua:

    weechat.register("test_lua", "FlashCode", "1.0", "GPL3", "Test script", "", "")
    weechat.print("", "Hello, from lua script!")

- Tcl:

    weechat::register "test_tcl" "FlashCode" "1.0" "GPL3" "Test script" "" ""
    weechat::print "" "Hello, from tcl script!"

- Guile (Scheme):

    (weechat:register "test_scheme" "FlashCode" "1.0" "GPL3" "Test script" "" "")
    (weechat:print "" "Hello, from scheme script!")

- JavaScript:

    weechat.register("test_js", "FlashCode", "1.0", "GPL3", "Test script", "", "");
    weechat.print("", "Hello, from javascript script!");

- PHP:

    weechat_register('test_php', 'FlashCode', '1.0', 'GPL3', 'Test script', '', '');
    weechat_print('', 'Hello, from PHP script!');

### 2.4. スクリプトのロード

スクリプトをロードするには "script" プラグインを使うことを推奨します。例:

    /script load script.py
    /script load script.pl
    /script load script.rb
    /script load script.lua
    /script load script.tcl
    /script load script.scm
    /script load script.js
    /script load script.php

プログラミング言語ごとの固有コマンドを利用することもできます:

    /python load script.py
    /perl load script.pl
    /ruby load script.rb
    /lua load script.lua
    /tcl load script.tcl
    /guile load script.scm
    /javascript load script.js
    /php load script.php

WeeChat の開始時にスクリプトを自動ロードするには language/autoload ディレクトリ内にリンクを作ってください。

例えば Python の場合:

    cd ~/.local/share/weechat/python/autoload
    ln -s ../script.py

注記 | `/script install` コマンドでスクリプトをインストールした場合、autoload ディレクトリ内にリンクが自動的に作成されます。

## 3. C API との違い

スクリプト API は C 言語プラグイン API とほぼ同じです。API に含まれる各関数の詳細 (プロトタイプ、引数、戻り値、例) については WeeChat プラグイン API リファレンス ↗を参照してください。

プラグイン と スクリプト の違いを理解することは重要です: プラグイン とはコンパイル済みバイナリファイルで `/plugin` コマンドを使ってロードします、これに対して スクリプト とはテキストファイルで例えば python プラグインであれば `/python` コマンドを使ってロードします。

例えば test.py スクリプトが WeeChat API 関数を呼び出す場合、以下の順に呼び出されます:

                   ┌──────────────────────┐        ╔══════════════════╗
                   │     python plugin    │        ║  WeeChat "core"  ║
                   ├────────────┬─────────┤        ╟─────────┐        ║
    test.py ─────► │ script API │  C API  │ ─────► ║  C API  │        ║
                   └────────────┴─────────┘        ╚═════════╧════════╝

WeeChat が test.py スクリプトで定義されたコールバックを呼び出す場合、順番は逆になります:

    ╔══════════════════╗        ┌──────────────────────┐
    ║  WeeChat "core"  ║        │     python plugin    │
    ║        ┌─────────╢        ├─────────┬────────────┤
    ║        │  C API  ║ ─────► │  C API  │ script API │ ─────► test.py
    ╚════════╧═════════╝        └─────────┴────────────┘

### 3.1. ポインタ

ご存知かもしれませんが、スクリプトには本当の意味での「ポインタ」はありません。このため API 関数がポインタを返す場合、スクリプトでは文字列に変換されます。

例えば、関数がポインタ 0x1234ab56 を返した場合、スクリプトは "0x1234ab56" という文字列を受け取ることになります。

API 関数の引数にポインタを与える場合、スクリプトではポインタを文字列型として渡さなければいけません。C 言語 API 関数を呼び出す前に C 言語プラグインがこれを本来のポインタ型に変換します。

空文字列や "0x0" を使うことも許されています。これらは C 言語で言うところの NULL と解釈されます。例えば、データをコアバッファ (WeeChat メインバッファ) に表示する場合、以下のようになります:

    weechat.prnt("", "hi!")

警告 | WeeChat の多くのスクリプト API 関数は計算量を減らすために、ポインタの値が正当なものか否かの確認を行いません。ポインタの正当性を確認することはプログラマが行わなければいけません。不正なポインタを利用した場合、細かなクラッシュレポートを目にすることになるでしょう ;)

### 3.2. コールバック

ほとんど全ての WeeChat コールバックは WEECHAT_RC_OK または WEECHAT_RC_ERROR を返さなければいけません (modifier コールバックは例外で、これは文字列を返します)。

C callbacks are using "callback_pointer" and "callback_data" arguments, which are pointers. In script API, there is only "callback_data" (or "data"), and it is a string instead of a pointer.

各プログラミング言語でコールバックを利用する例:

- Python:

    def timer_cb(data, remaining_calls):
        weechat.prnt("", "timer! data=%s" % data)
        return weechat.WEECHAT_RC_OK

    weechat.hook_timer(1000, 0, 1, "timer_cb", "test")

- Perl:

    sub timer_cb {
        my ($data, $remaining_calls) = @_;
        weechat::print("", "timer! data=$data");
        return weechat::WEECHAT_RC_OK;
    }

    weechat::hook_timer(1000, 0, 1, "timer_cb", "test");

- Ruby:

    def timer_cb(data, remaining_calls)
      Weechat.print("", "timer! data=#{data}");
      return Weechat::WEECHAT_RC_OK
    end

    Weechat.hook_timer(1000, 0, 1, "timer_cb", "test");

- Lua:

    function timer_cb(data, remaining_calls)
        weechat.print("", "timer! data="..data)
        return weechat.WEECHAT_RC_OK
    end

    weechat.hook_timer(1000, 0, 1, "timer_cb", "test")

- Tcl:

    proc timer_cb { data remaining_calls } {
        weechat::print {} "timer! data=$data"
        return $::weechat::WEECHAT_RC_OK
    }

    weechat::hook_timer 1000 0 1 timer_cb test

- Guile (Scheme):

    (define (timer_cb data remaining_calls)
      (weechat:print "" (string-append "timer! data=" data))
      weechat:WEECHAT_RC_OK
    )

    (weechat:hook_timer 1000 0 1 "timer_cb" "test")

- JavaScript:

    function timer_cb(data, remaining_calls) {
        weechat.print("", "timer! data=" + data);
        return weechat.WEECHAT_RC_OK;
    }

    weechat.hook_timer(1000, 0, 1, "timer_cb", "test");

- PHP:

    $timer_cb = function ($data, $remaining_calls) {
        weechat_print('', 'timer! data=' . $data);
        return WEECHAT_RC_OK;
    };

    weechat_hook_timer(1000, 0, 1, $timer_cb, 'test');

## 4. スクリプト API

API に含まれる関数の詳しい情報は WeeChat プラグイン API リファレンス ↗を参照してください。

### 4.1. 関数

スクリプト API に含まれる関数のリスト:

- register

- plugin_get_name

- charset_set

- iconv_to_internal

- iconv_from_internal

- gettext

- ngettext

- strlen_screen

- string_match

- string_match_list

- string_has_highlight

- string_has_highlight_regex

- string_mask_to_regex

- string_format_size

- string_parse_size

- string_color_code_size

- string_remove_color

- string_is_command_char

- string_input_for_buffer

- string_eval_expression

- string_eval_path_home

- mkdir_home

- mkdir

- mkdir_parents

- list_new

- list_add

- list_search

- list_search_pos

- list_casesearch

- list_casesearch_pos

- list_get

- list_set

- list_next

- list_prev

- list_string

- list_size

- list_remove

- list_remove_all

- list_free

- config_new

- config_set_version

- config_new_section

- config_search_section

- config_new_option

- config_search_option

- config_string_to_boolean

- config_option_reset

- config_option_set

- config_option_set_null

- config_option_unset

- config_option_rename

- config_option_get_string

- config_option_get_pointer

- config_option_is_null

- config_option_default_is_null

- config_boolean

- config_boolean_default

- config_boolean_inherited

- config_integer

- config_integer_default

- config_integer_inherited

- config_string

- config_string_default

- config_string_inherited

- config_color

- config_color_default

- config_color_inherited

- config_enum

- config_enum_default

- config_enum_inherited

- config_write_option

- config_write_line

- config_write

- config_read

- config_reload

- config_option_free

- config_section_free_options

- config_section_free

- config_free

- config_get

- config_get_plugin

- config_is_set_plugin

- config_set_plugin

- config_set_desc_plugin

- config_unset_plugin

- key_bind

- key_unbind

- prefix

- color

- prnt

- prnt_date_tags

- prnt_datetime_tags

- prnt_y

- prnt_y_date_tags

- prnt_y_datetime_tags

- log_print

- hook_command

- hook_completion

- hook_completion_get_string

- hook_completion_list_add

- hook_command_run

- hook_timer

- hook_fd

- hook_process

- hook_process_hashtable

- hook_url

- hook_connect

- hook_line

- hook_print

- hook_signal

- hook_signal_send

- hook_hsignal

- hook_hsignal_send

- hook_config

- hook_modifier

- hook_modifier_exec

- hook_info

- hook_info_hashtable

- hook_infolist

- hook_focus

- hook_set

- unhook

- unhook_all

- buffer_new

- buffer_new_props

- buffer_search

- buffer_search_main

- current_buffer

- buffer_clear

- buffer_close

- buffer_merge

- buffer_unmerge

- buffer_get_integer

- buffer_get_string

- buffer_get_pointer

- buffer_set

- buffer_string_replace_local_var

- buffer_match_list

- line_search_by_id

- current_window

- window_search_with_buffer

- window_get_integer

- window_get_string

- window_get_pointer

- window_set_title

- nicklist_add_group

- nicklist_search_group

- nicklist_add_nick

- nicklist_search_nick

- nicklist_remove_group

- nicklist_remove_nick

- nicklist_remove_all

- nicklist_group_get_integer

- nicklist_group_get_string

- nicklist_group_get_pointer

- nicklist_group_set

- nicklist_nick_get_integer

- nicklist_nick_get_string

- nicklist_nick_get_pointer

- nicklist_nick_set

- bar_item_search

- bar_item_new

- bar_item_update

- bar_item_remove

- bar_search

- bar_new

- bar_set

- bar_update

- bar_remove

- command

- command_options

- completion_new

- completion_search

- completion_get_string

- completion_set

- completion_list_add

- completion_free

- info_get

- info_get_hashtable

- infolist_new

- infolist_new_item

- infolist_new_var_integer

- infolist_new_var_string

- infolist_new_var_pointer

- infolist_new_var_time

- infolist_search_var

- infolist_get

- infolist_next

- infolist_prev

- infolist_reset_item_cursor

- infolist_fields

- infolist_integer

- infolist_string

- infolist_pointer

- infolist_time

- infolist_free

- hdata_get

- hdata_get_var_offset

- hdata_get_var_type_string

- hdata_get_var_array_size

- hdata_get_var_array_size_string

- hdata_get_var_hdata

- hdata_get_list

- hdata_check_pointer

- hdata_move

- hdata_search

- hdata_char

- hdata_integer

- hdata_long

- hdata_longlong

- hdata_string

- hdata_pointer

- hdata_time

- hdata_hashtable

- hdata_compare

- hdata_update

- hdata_get_string

- upgrade_new

- upgrade_write_object

- upgrade_read

- upgrade_close

### 4.2. 定数

スクリプト API に含まれる定数のリスト:

Constant | Type | Value
--- | --- | ---
WEECHAT_RC_OK | integer | `0`
WEECHAT_RC_OK_EAT | integer | `1`
WEECHAT_RC_ERROR | integer | `-1`
WEECHAT_CONFIG_READ_OK | integer | `0`
WEECHAT_CONFIG_READ_MEMORY_ERROR | integer | `-1`
WEECHAT_CONFIG_READ_FILE_NOT_FOUND | integer | `-2`
WEECHAT_CONFIG_WRITE_OK | integer | `0`
WEECHAT_CONFIG_WRITE_ERROR | integer | `-1`
WEECHAT_CONFIG_WRITE_MEMORY_ERROR | integer | `-2`
WEECHAT_CONFIG_OPTION_SET_OK_CHANGED | integer | `2`
WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE | integer | `1`
WEECHAT_CONFIG_OPTION_SET_ERROR | integer | `0`
WEECHAT_CONFIG_OPTION_SET_OPTION_NOT_FOUND | integer | `-1`
WEECHAT_CONFIG_OPTION_UNSET_OK_NO_RESET | integer | `0`
WEECHAT_CONFIG_OPTION_UNSET_OK_RESET | integer | `1`
WEECHAT_CONFIG_OPTION_UNSET_OK_REMOVED | integer | `2`
WEECHAT_CONFIG_OPTION_UNSET_ERROR | integer | `-1`
WEECHAT_LIST_POS_SORT | string | `sort`
WEECHAT_LIST_POS_BEGINNING | string | `beginning`
WEECHAT_LIST_POS_END | string | `end`
WEECHAT_HOTLIST_LOW | string | `0`
WEECHAT_HOTLIST_MESSAGE | string | `1`
WEECHAT_HOTLIST_PRIVATE | string | `2`
WEECHAT_HOTLIST_HIGHLIGHT | string | `3`
WEECHAT_HOOK_PROCESS_RUNNING | integer | `-1`
WEECHAT_HOOK_PROCESS_ERROR | integer | `-2`
WEECHAT_HOOK_CONNECT_IPV6_DISABLE | integer | `0`
WEECHAT_HOOK_CONNECT_IPV6_AUTO | integer | `1`
WEECHAT_HOOK_CONNECT_IPV6_FORCE | integer | `2`
WEECHAT_HOOK_CONNECT_OK | integer | `0`
WEECHAT_HOOK_CONNECT_ADDRESS_NOT_FOUND | integer | `1`
WEECHAT_HOOK_CONNECT_IP_ADDRESS_NOT_FOUND | integer | `2`
WEECHAT_HOOK_CONNECT_CONNECTION_REFUSED | integer | `3`
WEECHAT_HOOK_CONNECT_PROXY_ERROR | integer | `4`
WEECHAT_HOOK_CONNECT_LOCAL_HOSTNAME_ERROR | integer | `5`
WEECHAT_HOOK_CONNECT_GNUTLS_INIT_ERROR | integer | `6`
WEECHAT_HOOK_CONNECT_GNUTLS_HANDSHAKE_ERROR | integer | `7`
WEECHAT_HOOK_CONNECT_MEMORY_ERROR | integer | `8`
WEECHAT_HOOK_CONNECT_TIMEOUT | integer | `9`
WEECHAT_HOOK_CONNECT_SOCKET_ERROR | integer | `10`
WEECHAT_HOOK_SIGNAL_STRING | string | `string`
WEECHAT_HOOK_SIGNAL_INT | string | `int`
WEECHAT_HOOK_SIGNAL_POINTER | string | `pointer`

## 5. 良くあるタスク

この章ではいくつかの良くあるタスクを例を交えて紹介します。ここでは API の一部の機能を使っています。完全なリファレンスは WeeChat プラグイン API リファレンス ↗を参照してください。

### 5.1. バッファ

#### メッセージの表示

WeeChat コアバッファに対して操作する場合、空文字列を使うことが多いです。他のバッファに対して操作する場合には、ポインタ (文字列型、ポインタ を参照) を与える必要があります。

例:

    # "hello" をコアバッファに表示
    weechat.prnt("", "hello")

    # "hello" をコアバッファに表示するが、ログファイルには書き込まない
    # (WeeChat バージョン 0.3.3 以上で利用可)
    weechat.prnt_date_tags("", 0, "no_log", "hello")

    # プレフィックス "==>" とメッセージ "hello" を現在のバッファに表示
    # (プレフックストメッセージはタブで区切ってください)
    weechat.prnt(weechat.current_buffer(), "==>\thello")

    # コアバッファにエラーメッセージを表示 (エラープレフィックスを利用)
    weechat.prnt("", "%swrong arguments" % weechat.prefix("error"))

    # コアバッファに色付きメッセージを表示
    weechat.prnt("", "text %syellow on blue" % weechat.color("yellow,blue"))

    # バッファを検索してメッセージを表示
    # (バッファの完全な名前は plugin.name のような形です、例えば: "irc.libera.#weechat")
    buffer = weechat.buffer_search("irc", "libera.#weechat")
    weechat.prnt(buffer, "message on #weechat channel")

    # ある IRC バッファを見つける他の方法 (推奨)
    # (サーバとチャンネルはコンマで区切ってください。)
    buffer = weechat.info_get("irc_buffer", "libera,#weechat")
    weechat.prnt(buffer, "message on #weechat channel")

注記 | Print 関数は Python では `prnt`、その他の言語では `print` と呼ばれます。

#### バッファにテキストを送信

テキストやコマンドをバッファに送信できます。これはテキストやコマンドをタイプして [Enter] を押すことに対応します。

例:

    # 現在のバッファでコマンド "/help" を実行 (結果は core バッファに出力されます)
    weechat.command("", "/help")

    # テキスト "hello" を #weechat IRC チャンネルに送信 (このチャンネルにいるユーザにはメッセージが見えます。)
    buffer = weechat.info_get("irc_buffer", "libera,#weechat")
    weechat.command(buffer, "hello")

#### 新規バッファの作成

スクリプトを使って新しいバッファを作成し、このバッファにメッセージを表示させることができます。

2 つのコールバックを定義できます (任意): データの入力時に呼び出されるもの (バッファでテキストを入力して [Enter] を押した時) と、バッファが閉じられたときに呼び出されるもの (例えば `/buffer close` した時等) です。

例:

    # データの入力時に呼び出されるコールバック
    def buffer_input_cb(data, buffer, input_data):
        # ...
        return weechat.WEECHAT_RC_OK

    # バッファが閉じられた時に呼び出されるコールバック
    def buffer_close_cb(data, buffer):
        # ...
        return weechat.WEECHAT_RC_OK

    # バッファの作成
    buffer = weechat.buffer_new("mybuffer", "buffer_input_cb", "", "buffer_close_cb", "")

    # タイトルの設定
    weechat.buffer_set(buffer, "title", "This is title for my buffer.")

    # ローカル変数 "no_log" に "1" を設定することでログ保存を無効化
    weechat.buffer_set(buffer, "localvar_set_no_log", "1")

#### バッファプロパティ

文字列、整数、ポインタ型のバッファプロパティを読むことができます。

例:

    buffer = weechat.current_buffer()

    number = weechat.buffer_get_integer(buffer, "number")
    name = weechat.buffer_get_string(buffer, "name")
    short_name = weechat.buffer_get_string(buffer, "short_name")

バッファに対するローカル変数を追加、ロード、削除することができます:

    # ローカル変数の追加
    weechat.buffer_set(buffer, "localvar_set_myvar", "my_value")

    # ローカル変数のロード
    myvar = weechat.buffer_get_string(buffer, "localvar_myvar")

    # ローカル変数の削除
    weechat.buffer_set(buffer, "localvar_del_myvar", "")

バッファに対するローカル変数を見るには、WeeChat で以下のコマンドを実行してください:

    /buffer listvar

### 5.2. フック

#### 新しいコマンドの追加

カスタムコマンドを追加するには `hook_command` を使ってください。追加したコマンドに対してカスタム補完テンプレートを定義できます。

例:

    def my_command_cb(data, buffer, args):
        # ...
        return weechat.WEECHAT_RC_OK

    hook = weechat.hook_command("myfilter", "description of myfilter",
        "[list] | [enable|disable|toggle [name]] | [add name plugin.buffer tags regex] | [del name|-all]",
        "description of arguments...",
        "list"
        " || enable %(filters_names)"
        " || disable %(filters_names)"
        " || toggle %(filters_names)"
        " || add %(filters_names) %(buffers_plugins_names)|*"
        " || del %(filters_names)|-all",
        "my_command_cb", "")

上で定義したコマンドを WeeChat で以下のように使うことができます:

    /help myfilter

    /myfilter arguments...

#### タイマーの追加

タイマーを追加するには `hook_timer` を使ってください。

例:

    def timer_cb(data, remaining_calls):
        # ...
        return weechat.WEECHAT_RC_OK

    # 1 分毎に (秒が 00 になった時に) 呼び出されるタイマー
    weechat.hook_timer(60 * 1000, 60, 0, "timer_cb", "")

#### バックグラウンドプロセスの実行

バックグラウンドプロセスを実行するには `hook_process` を使います。コールバックはデータの準備が整った時点で呼び出されます。複数回呼び出されることもあります。

コールバックの最後の呼び出しでは return_code が 0 か正の値に設定されています。これはコマンドのリターンコードになります。

例:

    def my_process_cb(data, command, return_code, out, err):
        if return_code == weechat.WEECHAT_HOOK_PROCESS_ERROR:
            weechat.prnt("", "Error with command '%s'" % command)
            return weechat.WEECHAT_RC_OK
        if return_code >= 0:
            weechat.prnt("", "return_code = %d" % return_code)
        if out:
            weechat.prnt("", "stdout: %s" % out)
        if err:
            weechat.prnt("", "stderr: %s" % err)
        return weechat.WEECHAT_RC_OK

    weechat.hook_process("/bin/ls -l /etc", 10 * 1000, "my_process_cb", "")

You can also call directly a script function that does something blocking, instead of an external command:

    def get_status(data):
        # do something blocking...
        # ...
        return "this is the result"

    def my_process_cb(data, command, return_code, out, err):
        if return_code == weechat.WEECHAT_HOOK_PROCESS_ERROR:
            weechat.prnt("", "Error with command '%s'" % command)
            return weechat.WEECHAT_RC_OK
        if return_code >= 0:
            weechat.prnt("", "return_code = %d" % return_code)
        if out:
            weechat.prnt("", "stdout: %s" % out)
        if err:
            weechat.prnt("", "stderr: %s" % err)
        return weechat.WEECHAT_RC_OK

    hook = weechat.hook_process("func:get_status", 5000, "my_process_cb", "")

#### URL 転送

WeeChat バージョン 0.3.7 以上で利用可。

URL をダウンロードする (または URL にポストする) には、関数 `hook_process` または URL 転送にオプションが必要な場合は `hook_process_hashtable` を使わなければいけません。

オプション無しの URL 転送の例: HTML ページの内容はコールバックの "out" 引数 (プロセスの標準出力) を通して渡されます。

    # Display latest stable version of WeeChat.
    weechat_latest_version = ""

    def weechat_process_cb(data, command, return_code, out, err):
        global weechat_latest_version
        if out:
            weechat_latest_version += out
        if return_code >= 0:
            weechat.prnt("", "Latest WeeChat version: %s" % weechat_latest_version)
        return weechat.WEECHAT_RC_OK

    weechat.hook_process("url:https://weechat.org/dev/info/stable/",
                         30 * 1000, "weechat_process_cb", "")

ヒント | All infos available about WeeChat are on this page ↗.

オプション有りの URL 転送の例: 最新の WeeChat 開発パッケージをファイル /tmp/weechat-devel.tar.gz にダウンロード:

    def my_process_cb(data, command, return_code, out, err):
        if return_code >= 0:
            weechat.prnt("", "End of transfer (return code = %d)" % return_code)
        return weechat.WEECHAT_RC_OK

    weechat.hook_process_hashtable("url:https://weechat.org/files/src/weechat-devel.tar.gz",
                                   {"file_out": "/tmp/weechat-devel.tar.gz"},
                                   30 * 1000, "my_process_cb", "")

URL 転送に関するより詳しい情報と利用可能なオプションは WeeChat プラグイン API リファレンス ↗の `hook_process` と `hook_process_hashtable` を参照してください。

### 5.3. 設定 / オプション

#### スクリプトのオプションを設定

オプションが設定されているかどうかを確認するには `config_is_set_plugin` 関数、オプションを設定するには `config_set_plugin` 関数を使います。

例:

    script_options = {
        "option1": "value1",
        "option2": "value2",
        "option3": "value3",
    }
    for option, default_value in script_options.items():
        if not weechat.config_is_set_plugin(option):
            weechat.config_set_plugin(option, default_value)

#### 変更の検出

ユーザがスクリプトオプションを変更したことを検出するには `hook_config` を使わなければいけません。

例:

    SCRIPT_NAME = "myscript"

    # ...

    def config_cb(data, option, value):
        """スクリプトオプションが変更されたときに呼び出されるコールバック"""
        # 例えば、スクリプト変数に対する全てのスクリプトオプションを読み込む等...
        # ...
        return weechat.WEECHAT_RC_OK

    # ...

    weechat.hook_config("plugins.var.python." + SCRIPT_NAME + ".*", "config_cb", "")
    # 他のスクリプト言語の場合は "python" を適当なもの (perl/ruby/lua/tcl/guile/javascript) に変更してください

#### WeeChat オプションのロード

`config_get` 関数はオプションへのポインタを返します。オプションの型に従って `config_string`、`config_boolean`、`config_integer`、`config_color` を呼び出さなければいけません。

    # 文字列型の場合
    weechat.prnt("", "value of option weechat.look.item_time_format is: %s"
                     % (weechat.config_string(weechat.config_get("weechat.look.item_time_format"))))

    # ブール型の場合
    weechat.prnt("", "value of option weechat.look.day_change is: %d"
                     % (weechat.config_boolean(weechat.config_get("weechat.look.day_change"))))

    # 整数型の場合
    weechat.prnt("", "value of option weechat.look.scroll_page_percent is: %d"
                     % (weechat.config_integer(weechat.config_get("weechat.look.scroll_page_percent"))))

    # 色型の場合
    weechat.prnt("", "value of option weechat.color.chat_delimiters is: %s"
                     % (weechat.config_color(weechat.config_get("weechat.color.chat_delimiters"))))

### 5.4. IRC

#### メッセージのキャッチ

IRC plugin sends four signals for a message received (`xxx` is IRC internal server name, `yyy` is IRC command name like JOIN, QUIT, PRIVMSG, 301, ..):

xxx,irc_in_yyy

  signal sent before processing message, only if message is not ignored

xxx,irc_in2_yyy

  signal sent after processing message, only if message is not ignored

xxx,irc_raw_in_yyy

  signal sent before processing message, even if message is ignored

xxx,irc_raw_in2_yyy

  signal sent after processing message, even if message is ignored

    def join_cb(data, signal, signal_data):
        # シグナル、例えば: "libera,irc_in2_join"
        # signal_data は IRC メッセージ、例えば: ":nick!user@host JOIN :#channel"
        server = signal.split(",")[0]
        msg = weechat.info_get_hashtable("irc_message_parse", {"message": signal_data})
        buffer = weechat.info_get("irc_buffer", "%s,%s" % (server, msg["channel"]))
        if buffer:
            weechat.prnt(buffer, "%s (%s) has joined this channel!" % (msg["nick"], msg["host"]))
        return weechat.WEECHAT_RC_OK

    # 全ての IRC サーバに対する JOIN メッセージをキャッチするにはサーバの指定に
    # "*" を使うと便利です。
    weechat.hook_signal("*,irc_in2_join", "join_cb", "")

#### メッセージの修正

IRC plugin sends two "modifiers" for a message received ("xxx" is IRC command), so that you can modify it:

irc_in_xxx

  modifier sent before charset decoding: use with caution, the string may contain invalid UTF-8 data; use only for raw operations on a message

irc_in2_xxx

  modifier sent after charset decoding, so the string received is always UTF-8 valid (recommended)

    def modifier_cb(data, modifier, modifier_data, string):
        # 全てのメッセージにサーバ名を追加する
        # (これは役に立ちませんが、例として!)
        return "%s %s" % (string, modifier_data)

    weechat.hook_modifier("irc_in2_privmsg", "modifier_cb", "")

警告 | 不正なメッセージは WeeChat をクラッシュさせ、深刻な問題を引き起こします!

#### メッセージの構文解析

WeeChat バージョン 0.3.4 以上で利用可。

"irc_message_parse" と呼ばれる info_hashtable を使って IRC メッセージを構文解析できます。

結果は以下のキーを持つハッシュテーブルです (例の値は以下のメッセージから作られました: `@time=2015-06-27T16:40:35.000Z :nick!user@host PRIVMSG #weechat :hello!`):

キー | Since WeeChat (1) | 説明 | 例
--- | --- | --- | ---
tags | 0.4.0 | メッセージに付けられたタグ (空にすることも可) | `time=2015-06-27T16:40:35.000Z`
tag_xxx | 3.3 | Unescaped value of tag "xxx" (one key per tag). | `2015-06-27T16:40:35.000Z`
message_without_tags | 0.4.0 | タグを除いたメッセージ (タグが付けられていなければメッセージと同じ) | `:nick!user@host PRIVMSG #weechat :hello!`
nick | 0.3.4 | 発信者のニックネーム | `nick`
user | 2.7 | The origin user. | `user`
host | 0.3.4 | 発信者のホスト (ニックネームを含む) | `nick!user@host`
command | 0.3.4 | コマンド (PRIVMSG、NOTICE、…​) | `PRIVMSG`
channel | 0.3.4 | 送信先チャンネル | `#weechat`
arguments | 0.3.4 | コマンド引数 (チャンネルを含む) | `#weechat :hello!`
text | 1.3 | テキスト (ユーザメッセージなど) | `hello!`
paramN | 3.4 | Command parameter (from 1 to N). | `#weechat`
num_params | 3.4 | Number of command parameters. | `2`
pos_command | 1.3 | メッセージ内における command のインデックス (command が見つからない場合 "-1") | `47`
pos_arguments | 1.3 | メッセージ内における arguments のインデックス (arguments が見つからない場合 "-1") | `55`
pos_channel | 1.3 | メッセージ内における channel のインデックス (channel が見つからない場合 "-1") | `55`
pos_text | 1.3 | メッセージ内における text のインデックス (text が見つからない場合 "-1") | `65`

注記 | (1) The key has been introduced in this WeeChat version.

    dict = weechat.info_get_hashtable(
        "irc_message_parse",
        {"message": "@time=2015-06-27T16:40:35.000Z;tag2=value\\sspace :nick!user@host PRIVMSG #weechat :hello!"})

    # dict == {
    #     "tags": "time=2015-06-27T16:40:35.000Z;tag2=value\\sspace",
    #     "tag_time": "2015-06-27T16:40:35.000Z",
    #     "tag_tag2": "value space",
    #     "message_without_tags": ":nick!user@host PRIVMSG #weechat :hello!",
    #     "nick": "nick",
    #     "user": "user",
    #     "host": "nick!user@host",
    #     "command": "PRIVMSG",
    #     "channel": "#weechat",
    #     "arguments": "#weechat :hello!",
    #     "text": "hello!",
    #     "param1": "#weechat",
    #     "param2": "hello!",
    #     "num_params": "2",
    #     "pos_command": "65",
    #     "pos_arguments": "73",
    #     "pos_channel": "73",
    #     "pos_text": "83",
    # }

### 5.5. 情報

#### WeeChat のバージョン

バージョンを確認する最良の方法は "version_number" を参照し、16 進数のバージョン番号と整数値比較することです。

例:

    version = weechat.info_get("version_number", "") or 0
    if int(version) >= 0x00030200:
        weechat.prnt("", "This is WeeChat 0.3.2 or newer")
    else:
        weechat.prnt("", "This is WeeChat 0.3.1 or older")

注記 | バージョン 0.3.1.1 以下の場合 info_get("version_number") は空文字列を返すため、値が空でないことを確認しなければいけません。

文字列でバージョンを使うには:

    # 例えば "Version 0.3.2" のような出力が得られます。
    weechat.prnt("", "Version %s" % weechat.info_get("version", ""))

#### その他の情報

    # WeeChat config directory, for example: "/home/user/.config/weechat"
    weechat.prnt("", "WeeChat config dir: %s" % weechat.info_get("weechat_config_dir", ""))

    # キーボードの不使用時間
    weechat.prnt("", "Inactivity since %s seconds" % weechat.info_get("inactivity", ""))

### 5.6. インフォリスト

#### インフォリストのロード

WeeChat や他のプラグインによって作られたインフォリストを読み込むことができます。

例:

    # バッファのリストを得るために "buffer" インフォリストを読み込む
    infolist = weechat.infolist_get("buffer", "", "")
    if infolist:
        while weechat.infolist_next(infolist):
            name = weechat.infolist_string(infolist, "name")
            weechat.prnt("", "buffer: %s" % name)
        weechat.infolist_free(infolist)

重要 | WeeChat は自動的にメモリを解放しません、インフォリストによって使われたメモリを解放するには、`infolist_free` を呼び出すことを忘れないでください。