Script: zncplayback.py

Add support for the ZNC Playback module.
Author: jazzpi — Version: 0.2.2 — License: GPL-3.0-or-later
For WeeChat ≥ 0.3.2.
Tags: irc, znc, py2, py3
Added: 2016-10-30 — Updated: 2022-02-05

Download GitHub Repository

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 Jasper v. Blanckenburg <jasper@mezzo.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

#
# Play back all messages the ZNC bouncer received since you last received a
# message locally
# (this requires the Playback module to be loaded on ZNC)
#
# Add znc.in/playback either to irc.server_default.capabilites or
# to the servers you want to use the Playback module on, then set
# plugins.var.python.zncplayback.servers
#
# History:
#
# 2022-02-02, Andreas Hackl <a@r0.at>:
#     v0.2.2: handle invalid json in SCRIPT_SAVEFILE
# 2021-05-06, Sébastien Helleu <flashcode@flashtux.org>:
#     v0.2.1: add compatibility with WeeChat >= 3.2 (XDG directories)
# 2019-07-09, Alyssa Ross <hi@alyssa.is>:
#     v0.2.0: Make script compatible with Python 3
# 2016-08-27, Jasper v. Blanckenburg <jasper@mezzo.de>:
#     v0.1.0: Initial release

from __future__ import print_function

SCRIPT_NAME = "zncplayback"
SCRIPT_AUTHOR = "jazzpi"
SCRIPT_VERSION = "0.2.2"
SCRIPT_LICENSE = "GPL3"
SCRIPT_DESCRIPTION = "Add support for the ZNC Playback module"

SCRIPT_SAVEFILE = "zncplayback_times.json"

import_ok = True

try:
    import weechat
except ImportError:
    print("This script must be run under WeeChat.")
    print("Get WeeChat now at: http://www.weechat.org/")
    import_ok = False

try:
    import json
    import os.path as path
    from time import time
except ImportError as message:
    print("Missing package(s) for {}: {}".format(SCRIPT_NAME, message))
    import_ok = False

# Script options
zncplayback_settings_default = {
    "servers": (
        "",
        "Comma-separated list of servers that playback should be fetched for")
}
zncplayback_settings = {}

zncplayback_hooks = {
    "config": None,
    "connected": None,
    "messages": {}
}
zncplayback_last_times = {}


def write_last_times():
    """Write the last message times of all servers to disk."""
    with open(SCRIPT_SAVEFILE, "w") as fh:
        json.dump(zncplayback_last_times, fh)


def read_last_times():
    """Read the last message times of all servers from disk."""
    global zncplayback_last_times
    try:
        with open(SCRIPT_SAVEFILE, "r") as fh:
            zncplayback_last_times = json.load(fh)
    except (json.decoder.JSONDecodeError,FileNotFoundError):
        for server in zncplayback_settings["servers"].split(","):
            zncplayback_last_times[server] = 0


def zncplayback_config_cb(data, option, value):
    """Update in-memory settings when a script option is changed."""
    global zncplayback_settings
    pos = option.rfind(".")
    if pos > 0:
        name = option[pos+1:]
        if name in zncplayback_settings:
            if name == "servers":
                old_servers = zncplayback_settings["servers"].split(",")
                new_servers = value.split(",")
                # Unhook signals for old servers
                removed_servers = \
                    [s for s in old_servers if s not in new_servers]
                for serv in removed_servers:
                    weechat.unhook(zncplayback_hooks["messages"][serv])
                    del zncplayback_last_times[serv]
                # Hook signals for new servers
                added_servers = \
                    [s for s in new_servers if s not in old_servers]
                for serv in added_servers:
                    zncplayback_hooks["messages"][serv] = weechat.hook_signal(
                        "{},irc_raw_in_PRIVMSG".format(serv),
                        "zncplayback_message_cb", serv)
                    zncplayback_last_times[serv] = 0
            zncplayback_settings[name] = value
    return weechat.WEECHAT_RC_OK


def zncplayback_connected_cb(data, signal, server):
    """Fetch playback after connecting to a server."""
    if server not in zncplayback_settings["servers"].split(","):
        return weechat.WEECHAT_RC_OK
    buf = weechat.buffer_search("irc", "server.{}".format(server))
    weechat.command(buf,
        "/msg *playback PLAY * {}".format(zncplayback_last_times[server]))
    return weechat.WEECHAT_RC_OK


def zncplayback_message_cb(server, signal, message):
    """Update last time for a server when a PRIVMSG is sent."""
    global zncplayback_last_times
    if server not in zncplayback_settings["servers"].split(","):
        return weechat.WEECHAT_RC_OK
    zncplayback_last_times[server] = int(time())
    write_last_times()
    return weechat.WEECHAT_RC_OK


if __name__ == "__main__" and import_ok:
    if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
                        SCRIPT_LICENSE, SCRIPT_DESCRIPTION, "", ""):
        options = {
            'directory': 'data',
        }
        SCRIPT_SAVEFILE = weechat.string_eval_path_home(
            '%%h/%s' % SCRIPT_SAVEFILE,
            {}, {}, options,
        )
        # Set default settings
        version = weechat.info_get("version_number", "") or 0
        for option, value in list(zncplayback_settings_default.items()):
            if weechat.config_is_set_plugin(option):
                zncplayback_settings[option] = weechat.config_get_plugin(
                    option)
            else:
                weechat.config_set_plugin(option, value[0])
                zncplayback_settings[option] = value[0]
            if int(version) >= 0x00030500:
                weechat.config_set_desc_plugin(
                    option,
                    "%s (default: \"%s\")" % (value[1], value[0]))

        read_last_times()
        zncplayback_hooks["config"] = weechat.hook_config(
            "plugins.var.python.%s.*" % SCRIPT_NAME, "zncplayback_config_cb",
            "")
        zncplayback_hooks["connected"] = weechat.hook_signal(
            "irc_server_connected", "zncplayback_connected_cb", "")
        for serv in zncplayback_settings["servers"].split(","):
            zncplayback_hooks["messages"][serv] = weechat.hook_signal(
                "{},irc_raw_in_PRIVMSG".format(serv),
                "zncplayback_message_cb", serv)

        # TODO: Unhook when unloading script